tag:blogger.com,1999:blog-65433972559134697842024-03-19T02:52:34.237-07:00Are you readyThoughts on Data Structure , Algorithm, concurrency, design and software development processAshkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.comBlogger119125tag:blogger.com,1999:blog-6543397255913469784.post-35863719701273988162024-02-04T07:02:00.000-08:002024-02-04T07:17:28.411-08:00Demystifying Vector Databases: The Magic of Meaningful Search<h2 style="text-align: left;"><span style="font-family: courier;">What is vector database ? </span></h2><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;">The digital world is awash in unstructured data.</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> text documents,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> social media posts,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> images,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> videos,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> audio recordings,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> and more. While traditional database excel at storing and retrieving neatly organised data</span><span style="background-color: white;"><span style="color: #1f1f1f;"><span style="white-space-collapse: preserve;">,</span></span></span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> they struggle with this messy,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> ever-growing sea of information.</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> Enter vector databases,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> a new breed of database designed to unlock the hidden meaning within unstructured data.</span></span></div><div><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"><span style="font-family: arial;"><br /></span></span></div><div><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;">While Large Language Models (LLMs) have brought vector databases to the forefront,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> their applications extend far beyond this exciting field.</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> Recommendation systems use vector databases to suggest products you might like based on your past purchases and browsing history,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> even if you haven't explicitly searched for those items.</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> Fraud detection systems leverage them to identify suspicious patterns in financial transactions,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> helping catch anomalies that might slip through traditional filters.</span></span></div><div><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"><span style="font-family: arial;"><br /></span></span></div><div><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"><span style="font-family: arial;"><br /></span></span></div><div><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;">But how do these databases work their magic?</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> It all starts with a clever trick:</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> representing data as multi-dimensional vectors,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> essentially numerical lists.</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> Imagine every data point as a location on a map.</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> Nearby points on the map represent similar data,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> regardless of the original format (text,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> image,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> etc.</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;">).</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> This is achieved through techniques like <b>word embeddings</b>,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> where words with similar meanings are mapped to close points in the vector space.</span></span></div><div><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"><span style="font-family: arial;"><br /></span></span></div><div><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;">Traditional keyword-based searches often miss the mark.</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> Imagine searching for "small,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> fleshy,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> and seedless" fruits.</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> No exact match exists,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> leaving you frustrated.</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> But a vector database understands the underlying meaning of your query.</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> </span></span></div><div><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"><span style="font-family: arial;"><br /></span></span></div><div><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;">It finds data points closest to the "small,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> fleshy,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> and seedless" vector,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> leading you to <b>grapes </b>or <b>kiwis</b>,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> even though those words weren't explicitly used.</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> This semantic search capability unlocks a new level of data exploration and analysis.</span></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEi5WGEghBHMpZ_SRXvCQF7ZWbV_Ku_ys6b1DHbIa-Zy27A_UpBm4asqoODMKSbrupWEi3EC2FRKAMVGhT19uXWfVnNi4qVLaw7ouXFw5aSOd2THv3ZEikNZT2tNI5HgZsASi_CBzQmkj-4Gv2-UeYklCYo_C1Ps0ZAMXtFow8n6royQgWhvFbJ9Ph-vU3xI" style="margin-left: auto; margin-right: auto;"><img alt="" data-original-height="406" data-original-width="948" height="274" src="https://blogger.googleusercontent.com/img/a/AVvXsEi5WGEghBHMpZ_SRXvCQF7ZWbV_Ku_ys6b1DHbIa-Zy27A_UpBm4asqoODMKSbrupWEi3EC2FRKAMVGhT19uXWfVnNi4qVLaw7ouXFw5aSOd2THv3ZEikNZT2tNI5HgZsASi_CBzQmkj-4Gv2-UeYklCYo_C1Ps0ZAMXtFow8n6royQgWhvFbJ9Ph-vU3xI=w640-h274" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Search - Legacy vs Semantic</td></tr></tbody></table><br /><br /></span></div><div><span style="font-family: courier;"><br /></span></div><span id="docs-internal-guid-8e6df756-7fff-f506-f365-2c9645514d04"><span face="Arial, sans-serif" style="font-size: 11pt; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-numeric: normal; font-variant-position: normal; vertical-align: baseline; white-space-collapse: preserve;"></span></span><div><span style="font-family: courier;"> </span></div><div><h2><span style="font-family: courier;">How vectors are created ?</span></h2><div style="text-align: left;"><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px;">But how do these magical numbers come to life?</span><span style="background-color: white; color: #1f1f1f; font-size: 16px;"> Enter embeddings,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px;"> numerical representations of data points created by deep learning models.</span><span style="background-color: white; color: #1f1f1f; font-size: 16px;"> Imagine feeding a vast collection of text documents to a sophisticated neural network.</span><span style="background-color: white;"><span style="color: #1f1f1f;"> It analyses the relationships between words,</span></span><span style="background-color: white; color: #1f1f1f; font-size: 16px;"> their context,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px;"> and their usage,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px;"> eventually generating unique vector representations,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px;"> or embeddings,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px;"> for each word.</span><span style="background-color: white; color: #1f1f1f; font-size: 16px;"> These embeddings capture not just the literal meaning of the word,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px;"> but also its nuances and semantic connections</span></span></div></div><div style="text-align: left;"><span style="font-family: courier;"><br /></span></div><div style="text-align: left;"><span style="font-family: courier;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjnAerJQGm5JOmx1WCo598NBRo9Yd9zAI7U3WEQnqD7n1hID1XnDu0LkP7RWGzrujMqnFzAeFtxDZ6PqJAzSqHLy69QF4RuxYwEF1RGdOr29vCHMA8WWN2jFmjgZNMSx6TeKg4SPTbPC9G7TtM54VmTPwkCs-taluz8Vxsw-V0Oq70ycjnAVEN2ZkzTNUSi" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="465" data-original-width="951" height="312" src="https://blogger.googleusercontent.com/img/a/AVvXsEjnAerJQGm5JOmx1WCo598NBRo9Yd9zAI7U3WEQnqD7n1hID1XnDu0LkP7RWGzrujMqnFzAeFtxDZ6PqJAzSqHLy69QF4RuxYwEF1RGdOr29vCHMA8WWN2jFmjgZNMSx6TeKg4SPTbPC9G7TtM54VmTPwkCs-taluz8Vxsw-V0Oq70ycjnAVEN2ZkzTNUSi=w640-h312" width="640" /></a></div><br /><br /></span></div><div style="text-align: left;"><span style="font-family: courier;"><br /></span></div><div style="text-align: left;"><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;">Generally, the last layer of deep learning models focuses on specific tasks like prediction or classification. But the true treasure trove of knowledge lies in the </span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;">second-to-last layer</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;">, often called the bottleneck or hidden layer. </span></span></div><div style="text-align: left;"><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"><span style="font-family: arial;"><br /></span></span></div><div style="text-align: left;"><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;">This layer holds a </span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;">condensed representation</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> of the input data, capturing the essential features and relationships learned during training. By strategically </span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;">removing the last layer</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> and accessing the information in this penultimate layer, we can extract </span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;">vector embeddings</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> that encapsulate the model's understanding of the data.</span></span></div><div style="text-align: left;"><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"><span style="font-family: arial;"><br /></span></span></div><div style="text-align: left;"><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"><span style="font-family: arial;">Higher dimensionality captures more information but requires more storage and computation, while lower dimensionality is space-efficient but might miss some nuances.</span></span></div><div style="text-align: left;"><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"><span style="font-family: arial;"><br /></span></span></div><div style="text-align: left;"><span face=""Google Sans", "Helvetica Neue", sans-serif" style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;">The key is to find the right balance between the </span><span face=""Google Sans", "Helvetica Neue", sans-serif" style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;">dimensionality</span><span face=""Google Sans", "Helvetica Neue", sans-serif" style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> (size) of the embeddings and the desired level of detail.</span></div><div style="text-align: left;"><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"><span style="font-family: arial;"><br /></span></span></div><div style="text-align: left;"><span face=""Google Sans", "Helvetica Neue", sans-serif" style="background-color: white; color: #1f1f1f; font-size: 16px;">Forget training your own model!</span><span face=""Google Sans", "Helvetica Neue", sans-serif" style="background-color: white; color: #1f1f1f; font-size: 16px;"> The world after Chat GPT offers a wealth of ready-made embedding models</span></div><div style="text-align: left;"><span face=""Google Sans", "Helvetica Neue", sans-serif" style="background-color: white; color: #1f1f1f; font-size: 16px;"><br /></span></div><div style="text-align: left;"><span style="background-color: white;"><span style="color: #1f1f1f; font-family: arial;"><span style="white-space-collapse: preserve;"><br /></span></span></span></div><div style="text-align: left;"><span style="background-color: white;"><span style="color: #1f1f1f; font-family: arial;"><span style="white-space-collapse: preserve;"><br /></span></span></span></div><div style="text-align: left;"><span style="background-color: white;"><span face="Google Sans, Helvetica Neue, sans-serif" style="color: #1f1f1f;"><span style="white-space-collapse: preserve;"><br /></span></span></span></div><div style="text-align: left;"><span style="background-color: white;"><span face="Google Sans, Helvetica Neue, sans-serif" style="color: #1f1f1f;"><span style="white-space-collapse: preserve;"><br /></span></span></span></div><div style="text-align: left;"><span face=""Google Sans", "Helvetica Neue", sans-serif" style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"><br /></span></div><div style="text-align: left;"><div class="separator" style="clear: both; font-family: courier; text-align: center;"><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgiMMzkNAcUzyjRPXlj1pnSyJ3guvqxp0oQ0eVekg6o7JdlmupFGOqm3rPGjAbYpcdqvFUf_Tb-nQ8_Pp9D98bAxWtOoFvzH1tEjsHc7gAHWowXGY7qgcRPCv8UsQN0w9_X3ZsZqqWQpo57eWsS46Cg_5hnQhho5FBdrKxaZ8pxTerTFRwMQgXKiKYMTIiS" style="margin-left: auto; margin-right: auto;"><img alt="" data-original-height="1234" data-original-width="1612" height="490" src="https://blogger.googleusercontent.com/img/a/AVvXsEgiMMzkNAcUzyjRPXlj1pnSyJ3guvqxp0oQ0eVekg6o7JdlmupFGOqm3rPGjAbYpcdqvFUf_Tb-nQ8_Pp9D98bAxWtOoFvzH1tEjsHc7gAHWowXGY7qgcRPCv8UsQN0w9_X3ZsZqqWQpo57eWsS46Cg_5hnQhho5FBdrKxaZ8pxTerTFRwMQgXKiKYMTIiS=w640-h490" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">How to get embeddings?</td></tr></tbody></table><br /><br /></div><div class="separator" style="clear: both; font-family: courier; text-align: center;"><br /></div><div class="separator" style="clear: both; font-family: courier; text-align: center;"><br /></div><div class="separator" style="clear: both; font-family: courier; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;"><h2 style="font-family: Times;"><span style="font-family: courier;">Use case vectors solve?</span></h2><div><p data-sourcepos="5:1-5:132" style="background-color: white; color: #1f1f1f; font-size: 16px; margin: 24px 0px; white-space-collapse: preserve; word-break: break-word;"><span style="font-family: arial;">Get ready to explore the diverse problems solvable with vector embeddings! These powerful representations go beyond text, unlocking:</span></p><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px;"></span></span><p data-sourcepos="7:1-7:2" style="background-color: white; color: #1f1f1f; font-size: 16px; margin: 24px 0px; white-space-collapse: preserve; word-break: break-word;"><span style="font-family: arial;">1. <b>Semantic Search</b>: Dive deeper than keywords. Find images, videos, or audio similar to your intent, not just literal phrases. Imagine searching for "peaceful nature scene" and discovering breathtaking waterfalls instead of generic landscapes.</span></p><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px;"></span></span><p data-sourcepos="9:1-9:166" style="background-color: white; color: #1f1f1f; font-size: 16px; margin: 24px 0px; white-space-collapse: preserve; word-break: break-word;"><span style="font-family: arial;">2. <b>Data Similarity Search</b>: Uncover hidden connections across non-text data. Quickly identify similar products, faces, or even medical scans, regardless of format.</span></p><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px;"></span></span><p data-sourcepos="11:1-11:223" style="background-color: white; margin: 24px 0px; word-break: break-word;"><span style="font-family: arial;"><span style="color: #1f1f1f;"><span style="white-space-collapse: preserve;">3. </span></span></span><span style="font-family: arial;"><span style="color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> <b>Personalised Recommendations</b>:</span><span style="color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> Get suggestions that truly understand you.</span><span style="color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> Vector embeddings power recommendation systems that learn your preferences and suggest items you'll genuinely love,</span><span style="color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> not just similar purchases</span></span></p><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px;"></span></span><p data-sourcepos="13:1-13:79" style="background-color: white; color: #1f1f1f; font-size: 16px; margin: 24px 0px; white-space-collapse: preserve; word-break: break-word;"><span style="font-family: arial;">4. <b>Retrieval-Augmented Generation (RAG)</b>: Bridge the gap between information retrieval and generation. Leverage vector embeddings to create summaries, translate languages, or even write different creative text formats based on specific requests. This is number #1 application of LLM powered apps.</span></p><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px;"></span></span><p data-sourcepos="15:1-15:226" style="background-color: white; color: #1f1f1f; font-size: 16px; margin: 24px 0px; white-space-collapse: preserve; word-break: break-word;"><span style="font-family: arial;">5. <b>Fraud and Anomaly Detection</b>: Spot suspicious activity faster. Vector embeddings help identify unusual patterns in transactions, financial data, or even network traffic, leading to improved security and fraud prevention.</span></p><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px;"></span></span><p data-sourcepos="17:1-17:185" style="background-color: white; color: #1f1f1f; font-size: 16px; margin: 24px 0px; white-space-collapse: preserve; word-break: break-word;"><span style="font-family: arial;">6.<b> Search Result Ranking</b>: Get the most relevant results first. Embeddings power search engines to understand your intent and rank results based on meaning, not just keyword matches.</span></p><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px;"></span></span><p data-sourcepos="19:1-19:194" style="background-color: white; color: #1f1f1f; font-size: 16px; margin: 24px 0px; white-space-collapse: preserve; word-break: break-word;"><span style="font-family: arial;">7.<b> Efficient Clustering</b>: Group similar data points effortlessly. Vector embeddings enable efficient clustering of large datasets, revealing hidden patterns and facilitating further analysis.</span></p><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px;"></span></span><p data-sourcepos="21:1-21:184" style="background-color: white; color: #1f1f1f; font-size: 16px; margin: 24px 0px; white-space-collapse: preserve; word-break: break-word;"><span style="font-family: arial;">And that's just the beginning! The potential of vector embeddings continues to expand, promising exciting solutions in areas like drug discovery, social network analysis, and more.</span></p></div><div><br /><span face=""Google Sans", "Helvetica Neue", sans-serif" style="background-color: white; color: #1f1f1f;"> </span></div><h2><span style="font-family: courier;">How Vector database uses vector?</span></h2><div><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;">Let's explore their first superpower: <b>semantic similarity</b>.</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> Unlike traditional keyword searches,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> vector databases understand meaning.</span></span></div><div><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"><br /></span></span></div><div><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;">You can input a vector,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> and the database returns vectors representing the most similar meaning content,</span><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"> not just exact matches.</span></span></div><div><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"><br /></span></span></div><div><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;">This is classic example from popular paper written in 2013 -<a href="Efficient Estimation of Word Representations in Vector Space" target="_blank"> </a></span></span><span style="color: #1f1f1f; font-family: arial;"><span style="white-space-collapse: preserve;"><a href="https://arxiv.org/abs/1301.3781" target="_blank">Efficient Estimation of Word Representations in Vector Space</a></span></span></div><div><span style="color: #1f1f1f; font-family: arial;"><span style="white-space-collapse: preserve;"><br /></span></span></div><div><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px; white-space-collapse: preserve;"><br /></span></span></div><div><h2 _ngcontent-ng-c815268145="" class="query-text ng-star-inserted" dir="ltr" style="background-color: #f0f4f9; color: #1f1f1f; font-size: 1rem; line-height: 1.5rem; margin-block: 0px; padding-top: 5px; white-space-collapse: preserve;"><div class="separator" style="clear: both; font-family: "Google Sans", "Helvetica Neue", sans-serif; font-weight: 400; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhwQ-Vl_dqaAG5L3IVL2SMWUVI7yy10GIbNaO1YMAO78AUMHqPF98Xk2UrfYHl2FW5Sf-JfWGUPv1SZSzJkwwVbxVhYC8kMKw_GMMZbWh5ZbaPhHhkCeF2snhXv5_BfXEwafHpf6ANonMcDK-KPrErdVvd-MSmnwFg0NR2YXtF8gIlCCbe2UagZkqUqAZ9j" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="740" data-original-width="846" height="350" src="https://blogger.googleusercontent.com/img/a/AVvXsEhwQ-Vl_dqaAG5L3IVL2SMWUVI7yy10GIbNaO1YMAO78AUMHqPF98Xk2UrfYHl2FW5Sf-JfWGUPv1SZSzJkwwVbxVhYC8kMKw_GMMZbWh5ZbaPhHhkCeF2snhXv5_BfXEwafHpf6ANonMcDK-KPrErdVvd-MSmnwFg0NR2YXtF8gIlCCbe2UagZkqUqAZ9j=w400-h350" width="400" /></a></div><div class="separator" style="clear: both; font-family: "Google Sans", "Helvetica Neue", sans-serif; font-weight: 400; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;"><p data-sourcepos="1:1-1:197" style="background-color: white; font-family: "Google Sans", "Helvetica Neue", sans-serif; font-weight: 400; margin: 0px 0px 24px; word-break: break-word;">Several algorithms can be used for calculating vector difference, each with its advantages and limitations depending on the specific application and data characteristics. Here are some common ones:</p><span face="Google Sans, Helvetica Neue, sans-serif"><span style="background-color: white; font-weight: 400; white-space-collapse: collapse;"></span></span><p data-sourcepos="3:1-3:244" style="background-color: white; font-family: "Google Sans", "Helvetica Neue", sans-serif; margin: 24px 0px; word-break: break-word;"><span style="font-weight: 500;">Jaccard similarity</span><br /><span style="font-weight: 400;"> This compares the proportion of shared elements between two binary vectors (containing only 0s and 1s), often used for comparing sets or sparse data.</span></p><div class="separator" style="clear: both; font-family: "Google Sans", "Helvetica Neue", sans-serif; text-align: center;"><br /></div><p data-sourcepos="3:1-3:244" style="background-color: white; font-family: "Google Sans", "Helvetica Neue", sans-serif; margin: 24px 0px; word-break: break-word;"><span style="font-weight: 400;"></span><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj1Cwm_q0Tun9X7AQaZP7UUd0kGu2kquOZCxEmyCJtvuhJ7qEk9_6Sxgiv1n7UJqX5bFZs1mAt57iRetILh1eeUFUp18XVNOjpw-d6YuWNO0qmGDt-j2LvyDZUIJQ1GvgmqO25hj-iEFuSgCWY03pBLf0YTTAmJzSE0Vd69oGyp_hzPSqDw6i91Utg9bD3O" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img alt="" data-original-height="366" data-original-width="236" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEj1Cwm_q0Tun9X7AQaZP7UUd0kGu2kquOZCxEmyCJtvuhJ7qEk9_6Sxgiv1n7UJqX5bFZs1mAt57iRetILh1eeUFUp18XVNOjpw-d6YuWNO0qmGDt-j2LvyDZUIJQ1GvgmqO25hj-iEFuSgCWY03pBLf0YTTAmJzSE0Vd69oGyp_hzPSqDw6i91Utg9bD3O=w302-h240" width="302" /></a></p><p data-sourcepos="3:1-3:244" style="background-color: white; font-family: "Google Sans", "Helvetica Neue", sans-serif; margin: 24px 0px; word-break: break-word;"><br /></p><p data-sourcepos="3:1-3:244" style="background-color: white; margin: 24px 0px; word-break: break-word;"><span style="font-family: arial;"><b style="color: #202122; font-size: 14px; white-space-collapse: collapse;">Hamming distance</b><span style="color: #202122; font-size: 14px; font-weight: 400; white-space-collapse: collapse;"> </span></span></p></div></h2><h2 _ngcontent-ng-c815268145="" class="query-text ng-star-inserted" dir="ltr" style="background-color: #f0f4f9; color: #1f1f1f; font-family: "Google Sans", "Helvetica Neue", sans-serif; font-size: 1rem; font-weight: 400; line-height: 1.5rem; margin-block: 0px; padding-top: 5px; white-space-collapse: preserve;">Between two strings or vectors of equal length is the number of positions at which the corresponding symbols are different. In other words, it measures the minimum number of substitutions required to change one string into the other, or equivalently, the minimum number of errors that could have transformed one string into the other</h2><div><br /></div><div><br /></div><div><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjWVYzdYMBp_7KDxju2Ug8F5Zwdu-G5FyPpqpq-0WYDFb4jkjzr7YrqC7Bs5_NAMPhFb4S7W0vQx5cyMsKpYwZZliP97BS0UN4gwJsKLAg_yIWd3uxB2CubQwjlXYdSH7fGPDei51y7iGKUZoyYIGrQQUku_54dg7lX5Fyfx1b_0t6mONmf13910yVtqrqR" style="background-color: #f0f4f9; font-family: "Google Sans", "Helvetica Neue", sans-serif; font-size: 16px; font-weight: 700; margin-left: 1em; margin-right: 1em; text-align: center; white-space-collapse: preserve;"><img alt="" data-original-height="696" data-original-width="928" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEjWVYzdYMBp_7KDxju2Ug8F5Zwdu-G5FyPpqpq-0WYDFb4jkjzr7YrqC7Bs5_NAMPhFb4S7W0vQx5cyMsKpYwZZliP97BS0UN4gwJsKLAg_yIWd3uxB2CubQwjlXYdSH7fGPDei51y7iGKUZoyYIGrQQUku_54dg7lX5Fyfx1b_0t6mONmf13910yVtqrqR" width="320" /></a></div><h2 _ngcontent-ng-c815268145="" class="query-text ng-star-inserted" dir="ltr" style="background-color: #f0f4f9; line-height: 1.5rem; margin-block: 0px; padding-top: 5px;"><div class="separator" style="clear: both; text-align: left;"><p data-sourcepos="3:1-3:244" style="background-color: white; color: #1f1f1f; font-family: "Google Sans", "Helvetica Neue", sans-serif; font-size: 1rem; margin: 24px 0px; white-space-collapse: preserve; word-break: break-word;">Euclidean distance<span style="font-weight: 400;">: This is the most straightforward and intuitive method, calculating the straight-line distance between two points in multidimensional space. It's computationally efficient but sensitive to data scaling and dimensionality.</span></p><p data-sourcepos="3:1-3:244" style="background-color: white; color: #1f1f1f; font-family: "Google Sans", "Helvetica Neue", sans-serif; font-size: 1rem; margin: 24px 0px; white-space-collapse: preserve; word-break: break-word;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhtoqP2pJGVkmbHwiLNA9LLRBQLEKOHx9reJXR_rfLeORcpZp9up9i-Q_JifvBu-a0nvf1G4eGDpOZx2wO3wWXDT-D7uWSIjX1uKywTSdbfxvvnJlO13Gi5u0bRcC1gTIXre8zlUX9RZ5DNRgQ_9xJaDN8wcqWmjh_ARqaHgJwgIfWyMz816bIsr1-0vI1S" style="font-weight: 400; margin-left: 1em; margin-right: 1em; text-align: center;"><img alt="" data-original-height="624" data-original-width="830" height="301" src="https://blogger.googleusercontent.com/img/a/AVvXsEhtoqP2pJGVkmbHwiLNA9LLRBQLEKOHx9reJXR_rfLeORcpZp9up9i-Q_JifvBu-a0nvf1G4eGDpOZx2wO3wWXDT-D7uWSIjX1uKywTSdbfxvvnJlO13Gi5u0bRcC1gTIXre8zlUX9RZ5DNRgQ_9xJaDN8wcqWmjh_ARqaHgJwgIfWyMz816bIsr1-0vI1S=w400-h301" width="400" /></a></p><p data-sourcepos="3:1-3:244" style="background-color: white; color: #1f1f1f; font-family: "Google Sans", "Helvetica Neue", sans-serif; font-size: 1rem; margin: 24px 0px; white-space-collapse: preserve; word-break: break-word;">Manhattan distance<span style="font-weight: 400;">: This measures the distance by summing the absolute differences between corresponding elements of the vectors. It's less sensitive to outliers than Euclidean distance but not as intuitive for representing geometric similarity.</span></p><p data-sourcepos="3:1-3:244" style="background-color: white; color: #1f1f1f; font-family: "Google Sans", "Helvetica Neue", sans-serif; font-size: 1rem; margin: 24px 0px; white-space-collapse: preserve; word-break: break-word;"><span style="font-weight: 400;"></span></p><div class="separator" style="clear: both; color: #1f1f1f; font-family: "Google Sans", "Helvetica Neue", sans-serif; font-size: 1rem; text-align: center; white-space-collapse: preserve;"><br /></div><div class="separator" style="clear: both; color: #1f1f1f; font-family: "Google Sans", "Helvetica Neue", sans-serif; font-size: 1rem; text-align: center; white-space-collapse: preserve;"><br /></div><div class="separator" style="clear: both; color: #1f1f1f; font-family: "Google Sans", "Helvetica Neue", sans-serif; font-size: 1rem; text-align: center; white-space-collapse: preserve;"><br /></div><div class="separator" style="clear: both; color: #1f1f1f; font-family: "Google Sans", "Helvetica Neue", sans-serif; font-size: 1rem; text-align: center; white-space-collapse: preserve;"><br /></div><div class="separator" style="clear: both; color: #1f1f1f; font-family: "Google Sans", "Helvetica Neue", sans-serif; font-size: 1rem; text-align: center; white-space-collapse: preserve;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj2qHgtBnsy68lWbv-DCcZkWWwnozhC_Ho5imVCouwkDV3QN-JURokfqckwqEkXlCaI5_-prWrzRXsPb8DrGnQNAgCcG2kc-z7PO1dx5CeBP7X1WP6_4BSTDkJ2g3BXJ3Rr2KZtZpq_B9JKhpxPhIed3OU6sCJK3CXP8TwsVwl6Zs1W5KnrSunTYyf7X0ZU" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="626" data-original-width="792" height="316" src="https://blogger.googleusercontent.com/img/a/AVvXsEj2qHgtBnsy68lWbv-DCcZkWWwnozhC_Ho5imVCouwkDV3QN-JURokfqckwqEkXlCaI5_-prWrzRXsPb8DrGnQNAgCcG2kc-z7PO1dx5CeBP7X1WP6_4BSTDkJ2g3BXJ3Rr2KZtZpq_B9JKhpxPhIed3OU6sCJK3CXP8TwsVwl6Zs1W5KnrSunTYyf7X0ZU=w400-h316" width="400" /><div class="separator" style="clear: both; text-align: center;"><br /></div><br /></a></div><br /><br /></div><div class="separator" style="clear: both; color: #1f1f1f; font-family: "Google Sans", "Helvetica Neue", sans-serif; font-size: 1rem; text-align: center; white-space-collapse: preserve;"><br /></div><p data-sourcepos="3:1-3:244" style="background-color: white; color: #1f1f1f; font-family: "Google Sans", "Helvetica Neue", sans-serif; font-size: 1rem; margin: 24px 0px; white-space-collapse: preserve; word-break: break-word;">Inner Product : <span style="font-weight: 400;"> This method is a mathematical operation that measures the </span><span style="font-weight: 500;">degree of similarity or alignment</span><span style="font-weight: 400;"> between two vectors.</span><span style="font-weight: 400;"> It tells you how "close" two vectors are in the multidimensional space they inhabit</span></p><p data-sourcepos="3:1-3:244" style="background-color: white; color: #1f1f1f; font-family: "Google Sans", "Helvetica Neue", sans-serif; font-size: 1rem; margin: 24px 0px; white-space-collapse: preserve; word-break: break-word;"><span style="font-weight: 400;"></span></p><div class="separator" style="clear: both; color: #1f1f1f; font-size: 1rem; text-align: center; white-space-collapse: preserve;"><br /></div><div class="separator" style="clear: both; color: #1f1f1f; font-size: 1rem; text-align: center; white-space-collapse: preserve;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjPD98wJvAil1N7zmXhakU_7xwqXKv-M7wdI6Bu1TU3uwsgGoZCR31FgqjuSxo384brTC6W3dtFvEs23zrY39MnnFnRyAsE8BPynwtBzFlGF7271hGRmJW7M3Kx5D4UXiXBmgBNNXbeSSLNtHV24voq-1oxUeGcojqufaLrHOaYA2o2uqft09eeQx2Gy-i2" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="704" data-original-width="788" height="357" src="https://blogger.googleusercontent.com/img/a/AVvXsEjPD98wJvAil1N7zmXhakU_7xwqXKv-M7wdI6Bu1TU3uwsgGoZCR31FgqjuSxo384brTC6W3dtFvEs23zrY39MnnFnRyAsE8BPynwtBzFlGF7271hGRmJW7M3Kx5D4UXiXBmgBNNXbeSSLNtHV24voq-1oxUeGcojqufaLrHOaYA2o2uqft09eeQx2Gy-i2=w400-h357" width="400" /></a></div><br /><br /><br /><p style="color: #1f1f1f; font-size: 1rem; white-space-collapse: preserve;"></p><p style="color: #1f1f1f; font-family: "Google Sans", "Helvetica Neue", sans-serif; font-size: 1rem; white-space-collapse: preserve;"></p><span face="Google Sans, Helvetica Neue, sans-serif" style="color: #1f1f1f; font-size: 1rem; white-space-collapse: preserve;"><span style="background-color: white; font-weight: 400; white-space-collapse: collapse;"></span></span><p data-sourcepos="7:1-7:47" style="background-color: white; color: #1f1f1f; font-family: "Google Sans", "Helvetica Neue", sans-serif; font-size: 1rem; margin: 24px 0px; white-space-collapse: preserve; word-break: break-word;">Cosine similarity<span style="font-weight: 400;">: This method measures the angle between two vectors, reflecting their directional similarity. It's independent of magnitude and useful when interpreting vectors as directions rather than exact positions.</span></p><p data-sourcepos="7:1-7:47" style="background-color: white; color: #1f1f1f; font-family: "Google Sans", "Helvetica Neue", sans-serif; font-size: 1rem; margin: 24px 0px; white-space-collapse: preserve; word-break: break-word;"><span style="font-weight: 400;"></span></p><div class="separator" style="clear: both; color: #1f1f1f; font-size: 1rem; text-align: center; white-space-collapse: preserve;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh_LeACKfmm0BMbZsrGfX3ygZfTG0bHloHP7ZHMYV22eDCl8678oxWWrigR6xJVVMh0MRM-0JgzLZHCwRFBt3Y1enN9QC0Url5CUUR71lbt92LYBTK-zKRX4t3jVngRa-MMscoCwomXb1oWUN9Z_gTSLl-8OaNdi_K3dc28chsWOMSiVm1Yy4yWVtwLj9WO" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="664" data-original-width="794" height="334" src="https://blogger.googleusercontent.com/img/a/AVvXsEh_LeACKfmm0BMbZsrGfX3ygZfTG0bHloHP7ZHMYV22eDCl8678oxWWrigR6xJVVMh0MRM-0JgzLZHCwRFBt3Y1enN9QC0Url5CUUR71lbt92LYBTK-zKRX4t3jVngRa-MMscoCwomXb1oWUN9Z_gTSLl-8OaNdi_K3dc28chsWOMSiVm1Yy4yWVtwLj9WO=w400-h334" width="400" /></a></div><br /><br /></div></div></h2><h2><span style="font-family: courier;">Conclusion</span></h2><h2 _ngcontent-ng-c815268145="" class="query-text ng-star-inserted" dir="ltr" style="background-color: #f0f4f9; line-height: 1.5rem; margin-block: 0px; padding-top: 5px;"><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; color: #1f1f1f; font-size: 1rem; text-align: left; white-space-collapse: preserve;"></div><div class="separator" style="clear: both; text-align: left;"><p data-sourcepos="5:1-5:221" style="background-color: white; color: #1f1f1f; font-size: 16px; font-weight: 400; margin: 24px 0px; white-space-collapse: preserve; word-break: break-word;"><span style="font-family: arial;">This post delves into the fascinating world of vector databases, equipping you with a solid understanding of their core concepts, vector creation methods, and similarity search algorithms.</span></p><span style="font-family: arial;"><span style="background-color: white; color: #1f1f1f; font-size: 16px; font-weight: 400;"></span></span><p data-sourcepos="7:1-7:144" style="background-color: white; color: #1f1f1f; font-size: 16px; font-weight: 400; margin: 24px 0px; white-space-collapse: preserve; word-break: break-word;"><span style="font-family: arial;">In the next section, we'll dive into the unique storage and retrieval mechanisms employed by vector databases. Unlike traditional databases that rely on B-trees or hash indexes, vector databases utilize innovative approaches specifically designed for efficient vector searches. Get ready to explore a whole new level of data exploration!</span></p></div><br /><p style="color: #1f1f1f; font-size: 1rem; white-space-collapse: preserve;"></p><span face="Google Sans, Helvetica Neue, sans-serif" style="color: #1f1f1f; font-size: 1rem; white-space-collapse: preserve;"><span style="background-color: white; font-weight: 400; white-space-collapse: collapse;"></span></span><p data-sourcepos="9:1-9:173" style="background-color: white; color: #1f1f1f; font-family: "Google Sans", "Helvetica Neue", sans-serif; font-size: 1rem; margin: 24px 0px; white-space-collapse: preserve; word-break: break-word;"></p><div class="separator" style="clear: both; color: #1f1f1f; font-size: 1rem; text-align: center; white-space-collapse: preserve;"><br /></div><p data-sourcepos="9:1-9:173" style="background-color: white; color: #1f1f1f; font-size: 1rem; margin: 24px 0px; white-space-collapse: preserve; word-break: break-word;"><br /></p><br /><p style="color: #1f1f1f; font-size: 1rem; white-space-collapse: preserve;"></p></div></h2></div><div><br /></div></div></div><div style="text-align: left;"><span style="font-family: courier;"><br /></span></div>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com0tag:blogger.com,1999:blog-6543397255913469784.post-17336451025541496552023-08-14T00:15:00.001-07:002023-08-14T00:29:11.269-07:00Multi Indexing<p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 0px 0px 1.25em; white-space-collapse: preserve;"><span style="font-family: courier;">Imagine you are the owner of a grocery store, which stocks a wide variety of products such as bakery items, dairy products, eggs, grains, vegetables, oils, frozen foods, and more.</span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 0px 0px 1.25em; white-space-collapse: preserve;"><span style="font-family: courier;"><br /></span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 0px 0px 1.25em; white-space-collapse: preserve;"><span style="font-family: courier;"></span></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhoGPkKJFFzimxByGGi28-GeKrbByNduI6URTtDmV-FBOHLxZ7Av_kdPzbobT3gRdJJO9KxfCAe18vnoPB9obesC9nDmQqwS-pW3Aol2ChRKbfzK-_Jn9fozVGL_uoBjkczfazy83RpRW1t1SlQo-QDeR6AAKVmBEgwmyyZvMF029bSdeU1FL4fHim3DrwX" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="168" data-original-width="300" height="224" src="https://blogger.googleusercontent.com/img/a/AVvXsEhoGPkKJFFzimxByGGi28-GeKrbByNduI6URTtDmV-FBOHLxZ7Av_kdPzbobT3gRdJJO9KxfCAe18vnoPB9obesC9nDmQqwS-pW3Aol2ChRKbfzK-_Jn9fozVGL_uoBjkczfazy83RpRW1t1SlQo-QDeR6AAKVmBEgwmyyZvMF029bSdeU1FL4fHim3DrwX=w400-h224" width="400" /></a></div><br /><br /><p></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px; white-space-collapse: preserve;"><span style="font-family: courier;">Now, you want to create a hybrid index that allows for fast lookups based on several attributes of the products, including:</span></p><ul style="text-align: left;"><li><span style="background-color: #f7f7f8; color: #374151; font-family: courier; font-size: 16px; white-space-collapse: preserve;">Product name or ID</span></li><li><span style="background-color: #f7f7f8; color: #374151; font-family: courier; font-size: 16px; white-space-collapse: preserve;">Product expiry date</span></li><li><span style="background-color: #f7f7f8; color: #374151; font-family: courier; font-size: 16px; white-space-collapse: preserve;">Discount percentage</span></li><li><span style="background-color: #f7f7f8; color: #374151; font-family: courier; font-size: 16px; white-space-collapse: preserve;">Fast-moving/selling items</span></li></ul><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px; white-space-collapse: preserve;"><span style="font-family: courier;">In essence, this index will enable both simple lookups and more advanced functionalities like ranking products.</span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px; white-space-collapse: preserve;"><span style="font-family: courier;">One crucial distinction to note between these types of lookups is that:</span></p><ol style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; counter-reset: list-number 0; display: flex; flex-direction: column; font-size: 16px; list-style: none; margin: 1.25em 0px; padding: 0px; white-space-collapse: preserve;"><li style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; counter-increment: list-number 1; display: block; margin-bottom: 0px; margin-top: 0px; min-height: 28px; padding-left: 0.375em;"><span style="font-family: courier;">A search by product name or ID will yield unique items, meaning each product is listed only once.</span></li><li style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; counter-increment: list-number 1; display: block; margin-bottom: 0px; margin-top: 0px; min-height: 28px; padding-left: 0.375em;"><span style="font-family: courier;">A search by expiry date, discounted products, fast-moving items, etc., will return results ordered by their respective values, and the values are not necessarily unique.</span></li></ol><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;"><span style="font-family: courier;">With this hybrid index in place, you'll be able to efficiently manage your inventory and cater to customer demands more effectively. Whether it's quickly finding a specific product or identifying the most popular items, this system will streamline your grocery store operations and enhance the overall shopping experience.</span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;"><span style="font-family: courier;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: courier;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiKjxeX7iZ7SrigN9DPYq5YMwI0zjO8NYPqv6rbp7SWCjQ52jvibaCGh2S9FGRYc1E96BaSy5vieBoDtpsDA0FhX5Hfml1DJsDU8JA_RrVxm4gvz7yOqWeSBoJ9-2EHintiXzISbLSmbP6Ij1HZK7v8URrBZehcEdWHWTWw5Y2DfytYKOEECdfeZVUyqynz" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="652" data-original-width="980" height="426" src="https://blogger.googleusercontent.com/img/a/AVvXsEiKjxeX7iZ7SrigN9DPYq5YMwI0zjO8NYPqv6rbp7SWCjQ52jvibaCGh2S9FGRYc1E96BaSy5vieBoDtpsDA0FhX5Hfml1DJsDU8JA_RrVxm4gvz7yOqWeSBoJ9-2EHintiXzISbLSmbP6Ij1HZK7v8URrBZehcEdWHWTWw5Y2DfytYKOEECdfeZVUyqynz=w640-h426" width="640" /></a></span></div><span style="font-family: courier;"><br /><br /></span><p></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;"><span style="font-family: courier;"><br /></span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;"><span style="font-family: courier;"><br /></span></p><h2 style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px 0px; text-align: left; white-space-collapse: preserve;"><span style="font-family: courier;">Which data structures ?</span></h2><div><span style="font-family: courier;"><br /></span></div><div><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 0px 0px 1.25em; white-space-collapse: preserve;"><span style="font-family: courier;">Databases can effectively manage these types of access patterns through the implementation of indexes on the query columns and the strategic use of the 'ORDER BY' clause. However, it's important to note that this approach comes with its own set of trade-offs, which we'll explore in a future post.</span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 0px 0px 1.25em; white-space-collapse: preserve;"><span style="font-family: courier;"><br /></span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 0px 0px 1.25em; white-space-collapse: preserve;"><span style="font-family: courier;">For now, let's direct our attention towards determining the most suitable data structure for the task at hand.</span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px; white-space-collapse: preserve;"><span style="font-family: courier;">When faced with the challenge of efficiently handling two distinct access patterns - key-value lookup and ordered lookup by some attribute - traditional data structures like Maps, Binary Search Trees, or Priority Queues may not individually fulfil all the requirements. As a solution, we can create a new data structure that combines the strengths of these structures into one, calling it <b><i>Treep (Tree with priority)</i></b>.</span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px; white-space-collapse: preserve;"><span style="font-family: courier;">Treep will have the following characteristics:</span></p><ol style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; counter-reset: list-number 0; display: flex; flex-direction: column; font-size: 16px; list-style: none; margin: 1.25em 0px; padding: 0px; white-space-collapse: preserve;"><li style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; counter-increment: list-number 1; display: block; margin-bottom: 0px; margin-top: 0px; min-height: 28px; padding-left: 0.375em;"><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 0px;"><span style="font-family: courier;"><b>Key-Value Lookup</b>: Like Maps, Treep will allow fast key-value lookups, enabling quick retrieval of data using unique identifiers.</span></p></li><li style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; counter-increment: list-number 1; display: block; margin-bottom: 0px; margin-top: 0px; min-height: 28px; padding-left: 0.375em;"><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 0px;"><span style="font-family: courier;"><b>Ordered Lookup by Attribute</b>: Similar to Binary Search Trees and Priority Queues, Treep will maintain the data in a sorted order based on a selected attribute. This will enable efficient access to elements in a specific order (e.g., ascending or descending) with respect to that attribute.</span></p></li></ol></div><div><span style="font-family: courier;"><br /></span></div><div><span style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space-collapse: preserve;"><span style="font-family: courier;">The interface of the Treep data structure may look like below</span></span></div><div><br /></div><div><div style="background-color: #1e1f22; color: #bcbec4; font-family: "JetBrains Mono", monospace; font-size: 9.8pt; white-space: pre;"><div style="font-size: 9.8pt;"><div style="font-size: 9.8pt;"><span style="color: #cf8e6d;">public interface </span>Treep<<span style="color: #16baac;">K</span>, <span style="color: #16baac;">V</span>> {<br /> <span style="color: #cf8e6d;">void </span><span style="color: #56a8f5;">add</span>(<span style="color: #16baac;">V </span>value);<br /><br /> Set<String> <span style="color: #56a8f5;">orderBy</span>();<br /><br /> <span style="color: #16baac;">V </span><span style="color: #56a8f5;">get</span>(<span style="color: #16baac;">K </span>key);<br /><br /> <span style="color: #16baac;">V </span><span style="color: #56a8f5;">top</span>(String attributeName);<br /><br /> <span style="color: #16baac;">V </span><span style="color: #56a8f5;">takeTop</span>(String attributeName);<br /><br /> <span style="color: #16baac;">V </span><span style="color: #56a8f5;">delete</span>(<span style="color: #16baac;">K </span>key);<br /><br /> Iterator<<span style="color: #16baac;">K</span>> <span style="color: #56a8f5;">keys</span>();<br /><br /> <span style="color: #cf8e6d;">int </span><span style="color: #56a8f5;">size</span>();<br />}</div></div></div></div><div><br /></div><div><span style="font-family: courier;">get - <span style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space-collapse: preserve;">This method efficiently handles the simple key-based access pattern. By providing a key as input, you can swiftly retrieve the associated value from the Treep. This operation is ideal for quick lookups of specific elements using their unique identifiers.</span></span></div><div><span style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space-collapse: preserve;"><span style="font-family: courier;"><br /></span></span></div><div><span style="font-family: courier;">top/taketop - T<span style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space-collapse: preserve;">his method caters to the access pattern of retrieving elements ordered by a specific attribute.</span></span></div><div><br /></div><div><br /></div><div><span style="font-family: courier;"><span style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space-collapse: preserve;"><br /></span></span></div><div><span style="font-family: courier;"><span style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space-collapse: preserve;">Code snippet using Treep </span></span></div><div><span style="font-family: courier;"><span style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space-collapse: preserve;"><br /></span></span></div><div><span style="font-family: courier;"><span style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space-collapse: preserve;"><div style="background-color: #1e1f22; color: #bcbec4; font-family: "JetBrains Mono", monospace; font-size: 9.8pt; text-wrap: nowrap;">Treep<String, Product> stores = <span style="color: #cf8e6d;">new </span>MapPriorityQueue<>(Product::name, Map.<span style="font-style: italic;">of</span>(<br /> <span style="color: #6aab73;">"discount"</span>, Comparator.<span style="font-style: italic;">comparing</span>(Product::discount).reversed(),<br /> <span style="color: #6aab73;">"price"</span>, Comparator.<span style="font-style: italic;">comparing</span>(Product::price).reversed()<br />));<br /><br /><br />Arrays.<span style="font-style: italic;">asList</span>(<br /> Product.<span style="font-style: italic;">of</span>(<span style="color: #6aab73;">"AXION Yellow"</span>, <span style="color: #2aacb8;">2.12f</span>, <span style="color: #2aacb8;">.10f</span>),<br /> Product.<span style="font-style: italic;">of</span>(<span style="color: #6aab73;">"Meji Fresh Milk 2L"</span>, <span style="color: #2aacb8;">6.9f</span>, <span style="color: #2aacb8;">0.0f</span>),<br /> Product.<span style="font-style: italic;">of</span>(<span style="color: #6aab73;">"red Chilli 100 G"</span>, <span style="color: #2aacb8;">1.14f</span>, <span style="color: #2aacb8;">.05f</span>),<br /> Product.<span style="font-style: italic;">of</span>(<span style="color: #6aab73;">"Fresh Cucumber"</span>, <span style="color: #2aacb8;">1.37f</span>, <span style="color: #2aacb8;">.01f</span>),<br /> Product.<span style="font-style: italic;">of</span>(<span style="color: #6aab73;">"China Garlic"</span>, <span style="color: #2aacb8;">1.93f</span>, <span style="color: #2aacb8;">0.0f</span>),<br /> Product.<span style="font-style: italic;">of</span>(<span style="color: #6aab73;">"Red Onion"</span>, <span style="color: #2aacb8;">1.19f</span>, <span style="color: #2aacb8;">0.07f</span>),<br /> Product.<span style="font-style: italic;">of</span>(<span style="color: #6aab73;">"Fuji Apple"</span>, <span style="color: #2aacb8;">3.14f</span>, <span style="color: #2aacb8;">.11f</span>),<br /> Product.<span style="font-style: italic;">of</span>(<span style="color: #6aab73;">"Banana"</span>, <span style="color: #2aacb8;">3.58f</span>, <span style="color: #2aacb8;">.12f</span>)<br />).forEach(stores::add);<br /><br /><span style="font-style: italic;">assertEquals</span>(Product.<span style="font-style: italic;">of</span>(<span style="color: #6aab73;">"Banana"</span>, <span style="color: #2aacb8;">3.58f</span>, <span style="color: #2aacb8;">.12f</span>), stores.top(<span style="color: #6aab73;">"discount"</span>));<br /><span style="font-style: italic;">assertEquals</span>(Product.<span style="font-style: italic;">of</span>(<span style="color: #6aab73;">"Meji Fresh Milk 2L"</span>, <span style="color: #2aacb8;">6.9f</span>, <span style="color: #2aacb8;">0.0f</span>), stores.top(<span style="color: #6aab73;">"price"</span>));</div></span></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">MapPriorityQueue is using Map and Priority queue to implement multi index feature.</span></div><div><span style="font-family: courier;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj6ZHw1z5s3xo4Uw4lPCmmKB-DnJsvUPjEpLAiFJ4wx3eoHTMzdab0iYWaJD9PEZPi9uTNs95T1jz5i9kcm7v2Oivpcr00Q_JbqHu9CKsPTjRHZutMqEof26w68Q1lPVrPBPwDvc-Faeul4LAaDxD3GDclc6AXxPWzUPnmnXU1mqA8gsQN9agwXBuL2VBpK" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="281" data-original-width="596" height="302" src="https://blogger.googleusercontent.com/img/a/AVvXsEj6ZHw1z5s3xo4Uw4lPCmmKB-DnJsvUPjEpLAiFJ4wx3eoHTMzdab0iYWaJD9PEZPi9uTNs95T1jz5i9kcm7v2Oivpcr00Q_JbqHu9CKsPTjRHZutMqEof26w68Q1lPVrPBPwDvc-Faeul4LAaDxD3GDclc6AXxPWzUPnmnXU1mqA8gsQN9agwXBuL2VBpK=w640-h302" width="640" /></a></div><br /><br /></div><br /><br /></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;">Full working code is available @ <a href="https://github.com/ashkrit/corejava/tree/master/explore/explore/src/main/java/org/example/explore/ds" target="_blank">github</a>. There are other ways to implement such data structure by using Map + SortedSet , Map + SkipList</span></div><div><span style="font-family: courier;"><br /></span></div><div><br /></div>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com0tag:blogger.com,1999:blog-6543397255913469784.post-91630777744931433072023-07-15T05:17:00.002-07:002023-07-15T05:24:44.229-07:00More choice using ChoiceFormat<p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 0px 0px 1.25em; white-space-collapse: preserve;">The conversion of numbers to text is a frequently encountered problem for engineers. It manifests in various scenarios, and some of the most prevalent examples include:</p><ul style="text-align: left;"><li><span face="Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"" style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space-collapse: preserve;">Converting a number to a day of the week</span></li><li><span face="Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"" style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space-collapse: preserve;">Converting a number to a month</span></li><li><span face="Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"" style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space-collapse: preserve;">Transforming a status into user-friendly text</span></li></ul><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;">Now, let's explore some solutions for addressing this problem, using the conversion of a number to a day of the week as an explanatory example.</p><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvify07nD_7-iIxtcaHTPwYmf4GjNs39ual1D6La-4OQcmGQ_2_cJdutVMug0rp7Yppb7lauKw6yWcoOLJ2KnCVQAbz1yySh9dGiIor7lBnplI2SGBIjiYnHaglAaahEN8My4FTXCHlFIQnruwZPSzG22dSwIR0we6ypw8mEpMu1pFl1FqRKuoWrQlJFHA/s280/download.jpeg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="180" data-original-width="280" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvify07nD_7-iIxtcaHTPwYmf4GjNs39ual1D6La-4OQcmGQ_2_cJdutVMug0rp7Yppb7lauKw6yWcoOLJ2KnCVQAbz1yySh9dGiIor7lBnplI2SGBIjiYnHaglAaahEN8My4FTXCHlFIQnruwZPSzG22dSwIR0we6ypw8mEpMu1pFl1FqRKuoWrQlJFHA/s1600/download.jpeg" width="280" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div></div><br /><br /></span></div><div><span style="font-family: courier;"><br /></span></div><h2 style="text-align: left;"><span style="font-family: courier;">- IF/Else/Switch</span></h2><div><span style="font-family: courier;"><br /></span></div><div><div style="background-color: #1e1f22; color: #bcbec4; font-family: "JetBrains Mono", monospace; font-size: 9.8pt; white-space: pre;"><span style="color: #cf8e6d;">public static </span>String <span style="color: #56a8f5;">toDayOfWeek</span>(<span style="color: #cf8e6d;">int </span>value) {<br /><br /> <span style="color: #cf8e6d;">if </span>(value == <span style="color: #2aacb8;">1</span>) {<br /> <span style="color: #cf8e6d;">return </span><span style="color: #6aab73;">"MON"</span>;<br /> } <span style="color: #cf8e6d;">else if </span>(value == <span style="color: #2aacb8;">2</span>) {<br /> <span style="color: #cf8e6d;">return </span><span style="color: #6aab73;">"TUE"</span>;<br /> } <span style="color: #cf8e6d;">else if </span>(value == <span style="color: #2aacb8;">3</span>) {<br /> <span style="color: #cf8e6d;">return </span><span style="color: #6aab73;">"WED"</span>;<br /> } <span style="color: #cf8e6d;">else if </span>(value == <span style="color: #2aacb8;">4</span>) {<br /> <span style="color: #cf8e6d;">return </span><span style="color: #6aab73;">"THU"</span>;<br /> } <span style="color: #cf8e6d;">else if </span>(value == <span style="color: #2aacb8;">5</span>) {<br /> <span style="color: #cf8e6d;">return </span><span style="color: #6aab73;">"FRI"</span>;<br /> } <span style="color: #cf8e6d;">else if </span>(value == <span style="color: #2aacb8;">6</span>) {<br /> <span style="color: #cf8e6d;">return </span><span style="color: #6aab73;">"SAT"</span>;<br /> } <span style="color: #cf8e6d;">else if </span>(value == <span style="color: #2aacb8;">7</span>) {<br /> <span style="color: #cf8e6d;">return </span><span style="color: #6aab73;">"WED"</span>;<br /> }<br /> <span style="color: #cf8e6d;">return </span><span style="color: #6aab73;">"You are on moon"</span>;<br /><br />}</div></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: courier;"><br /></span></div><div><span face="Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"" style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space-collapse: preserve;">While this solution may serve as a decent starting point, it is worth noting that even your grandfather might not approve of it.</span></div><div><span face="Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"" style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space-collapse: preserve;"><br /></span></div><div><h2><span style="font-family: courier;">- Maps</span></h2><div><span style="font-family: courier;"><br /></span></div><div><div style="background-color: #1e1f22; color: #bcbec4; font-family: "JetBrains Mono", monospace; font-size: 9.8pt; white-space: pre;"><span style="color: #cf8e6d;">public static </span>String <span style="color: #56a8f5;">toDayOfWeek</span>(<span style="color: #cf8e6d;">int </span>value) {<br /><br /> Map<Integer, String> dayOfWeek = <span style="color: #cf8e6d;">new </span>HashMap<>() {<br /> {<br /> <span style="color: #cf8e6d;">int </span>index = <span style="color: #2aacb8;">1</span>;<br /> put(index++, <span style="color: #6aab73;">"MON"</span>);<br /> put(index++, <span style="color: #6aab73;">"TUE"</span>);<br /> put(index++, <span style="color: #6aab73;">"WED"</span>);<br /> put(index++, <span style="color: #6aab73;">"THU"</span>);<br /> put(index++, <span style="color: #6aab73;">"FRI"</span>);<br /> put(index++, <span style="color: #6aab73;">"SAT"</span>);<br /> put(index++, <span style="color: #6aab73;">"SUN"</span>);<br /> }<br /> };<br /><br /> <span style="color: #cf8e6d;">return </span>dayOfWeek<br /> .getOrDefault(value, <span style="color: #6aab73;">"You are on moon"</span>);<br /><br />}</div></div><h2><br class="Apple-interchange-newline" /><span face="Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"" style="background-color: #f7f7f8; color: #374151; font-size: 16px; font-weight: 400; white-space-collapse: preserve;">This solution appears to be an improvement and can be considered a good option, especially when the key is dynamic. However, it is important to note that maintaining this solution can become challenging over time.</span></h2><div><span face="Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"" style="background-color: #f7f7f8; color: #374151; font-size: 16px; font-weight: 400; white-space-collapse: preserve;"><br /></span></div><h2><span style="font-family: courier;">- Enums</span></h2></div><div><span style="font-family: courier;"><br /></span></div><div><div style="background-color: #1e1f22; color: #bcbec4; font-family: "JetBrains Mono", monospace; font-size: 9.8pt; white-space: pre;"><span style="color: #cf8e6d;">public enum </span>DayOfWeek {<br /> <span style="color: #c77dbb; font-style: italic;">MONDAY</span>,<br /> <span style="color: #c77dbb; font-style: italic;">TUESDAY</span>,<br /> <span style="color: #c77dbb; font-style: italic;">WEDNESDAY</span>,<br /> <span style="color: #c77dbb; font-style: italic;">THURSDAY</span>,<br /> <span style="color: #c77dbb; font-style: italic;">FRIDAY</span>,<br /> <span style="color: #c77dbb; font-style: italic;">SATURDAY</span>,<br /> <span style="color: #c77dbb; font-style: italic;">SUNDAY</span>;<br /> <span style="color: #cf8e6d;">private static final </span>DayOfWeek[] <span style="color: #c77dbb; font-style: italic;">ENUMS </span>= DayOfWeek.<span style="font-style: italic;">values</span>();<br /><br /> <span style="color: #cf8e6d;">public static </span>DayOfWeek <span style="color: #56a8f5;">of</span>(<span style="color: #cf8e6d;">int </span>dayOfWeek) {<br /> <span style="color: #cf8e6d;">return </span><span style="color: #c77dbb; font-style: italic;">ENUMS</span>[dayOfWeek - <span style="color: #2aacb8;">1</span>];<br /> }<br />}</div></div><div><br /></div><div><span face="Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"" style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space-collapse: preserve;">This approach seems quite elegant and aligns with the way JDK handles similar scenarios. It offers several advantages, such as leveraging data types to ensure compile-time safety. This not only enhances maintainability but also provides the benefit of catching errors during compilation. However, one potential drawback of this approach is that it can pose challenges when it comes to extending functionality due to the strong type safety constraints.</span></div><div><span face="Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"" style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space-collapse: preserve;"><br /></span></div><div><h2><span style="font-family: courier;">- Choice Format</span></h2></div><div><span style="font-family: courier;"><br /></span></div><div><span face="Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"" style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space-collapse: preserve;">The choice format is a relatively new feature introduced in JDK 17+, and it offers intriguing solutions for tackling this problem. Before delving into the intricacies of how it works, let's examine some code examples to get a better understanding.</span></div><div><span style="font-family: courier;"><br /></span></div><div><div style="background-color: #1e1f22; color: #bcbec4; font-family: "JetBrains Mono", monospace; font-size: 9.8pt; white-space: pre;"><div style="font-size: 9.8pt;"><div style="font-size: 9.8pt;"><span style="color: #cf8e6d;">public static </span>String <span style="color: #56a8f5;">toDayOfWeek</span>(<span style="color: #cf8e6d;">int </span>value) {<br /> <span style="color: #cf8e6d;">double</span>[] limits = {<span style="color: #2aacb8;">1</span>, <span style="color: #2aacb8;">2</span>, <span style="color: #2aacb8;">3</span>, <span style="color: #2aacb8;">4</span>, <span style="color: #2aacb8;">5</span>, <span style="color: #2aacb8;">6</span>, <span style="color: #2aacb8;">7</span>};<br /> String[] formats = {<span style="color: #6aab73;">"Mon"</span>, <span style="color: #6aab73;">"Tue"</span>, <span style="color: #6aab73;">"Wed"</span>, <span style="color: #6aab73;">"Thur"</span>, <span style="color: #6aab73;">"Fri"</span>, <span style="color: #6aab73;">"Sat"</span>, <span style="color: #6aab73;">"Sun"</span>};<br /><br /> ChoiceFormat form = <span style="color: #cf8e6d;">new </span>ChoiceFormat(limits, formats);<br /> <span style="color: #cf8e6d;">return </span>form.format(value);<br />}</div></div></div></div><div><span style="font-family: courier;"><br /></span></div><div><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 0px 0px 1.25em; white-space-collapse: preserve;">This solution solves the problem by employing a straightforward approach: using pairs of arrays or vectors to map numbers to their corresponding strings.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px; white-space-collapse: preserve;">At first glance, it may not seem particularly remarkable, resembling a Map structure where the keys are represented by one array and the values by another. However, the true test begins when we pass values that are not present in the array.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;">Now, let's speculate. What do you think this function would return if we were to pass 0 or 100? One possibility could be "Undefined," which would hold true if this were JavaScript. Another option could be null or a NullPointerException, which is a close guess given your familiarity with Java. However, this is where the solution gets interesting.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;">Lets look at the output for 1/2/0/100</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 1.25em 0px 0px;"><span face="Söhne, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif, Helvetica Neue, Arial, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji" style="color: #374151;"><span style="white-space-collapse: preserve;">1 -> Mon
2 -> Tue
<b>0 -> Mon</b>
<b>100 -> Sun</b></span></span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 0px 0px 1.25em; white-space-collapse: preserve;"><span face="Söhne, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif, Helvetica Neue, Arial, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji"><br class="Apple-interchange-newline" /></span><span style="font-family: arial;">The output obtained from this solution provides some valuable insights into how ChoiceFormat operates. For example, when we pass 0, it returns "Monday," and when we pass 100, it returns "Sunday."</span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px; white-space-collapse: preserve;"><span style="font-family: arial;">Based on these results, you might start getting an idea of how ChoiceFormat functions. It relies on a few key elements:</span></p><ul style="text-align: left;"><li><span style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space-collapse: preserve;"><span style="font-family: arial;"><b>Ascending limits array</b>: The limits array is arranged in ascending order, defining the intervals.</span></span></li><li><span style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space-collapse: preserve;"><span style="font-family: arial;"><b>Format array:</b> This array has the same size as the limits array and contains the corresponding text representations for each interval.</span></span></li><li><span style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space-collapse: preserve;"><span style="font-family: arial;"><b>Interval Behaviour</b>: This values in limit array represent half-open interval, meaning that the lower limit is inclusive while upper limit is exclusive.</span></span></li></ul><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;"><span style="font-family: arial;">These factors play a crucial role in determining the appropriate text representation based on the input value within the defined intervals.</span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;"><span style="font-family: arial;">Lets look at half-open interval match with below function</span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;"><span style="font-family: arial;"><br /></span></p><div style="background-color: #1e1f22; color: #bcbec4; font-family: "JetBrains Mono", monospace; font-size: 9.8pt; white-space: pre;"><span style="color: #cf8e6d;">public static </span>String <span style="color: #56a8f5;">weekDayOrWeekend</span>(<span style="color: #cf8e6d;">int </span>value) {<br /> <span style="color: #cf8e6d;">double</span>[] limits = {<span style="color: #2aacb8;">1</span>, <span style="color: #2aacb8;">6</span>};<br /> String[] formats = {<span style="color: #6aab73;">"WeekDay"</span>, <span style="color: #6aab73;">"Weekend"</span>};<br /><br /> ChoiceFormat form = <span style="color: #cf8e6d;">new </span>ChoiceFormat(limits, formats);<br /> <span style="color: #cf8e6d;">return </span>form.format(value);<br />}</div><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 0px 0px 1.25em; white-space-collapse: preserve;"><br /></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 0px 0px 1.25em; white-space-collapse: preserve;">This particular implementation exhibits an interesting behavior: for values less than 5, the function returns "Weekday," while for values 6 and above, it returns "Weekend."</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px; white-space-collapse: preserve;">Isn't it fascinating how ChoiceFormat manages to accomplish this range-based search and deliver the appropriate result? It's remarkable how this small utility class can perform such a useful trick.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;">Let's consider one more simple example before delving into the greater capabilities of this utility class.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;"><span style="font-family: arial;"><br /></span></p><div style="background-color: #1e1f22; color: #bcbec4; font-family: "JetBrains Mono", monospace; font-size: 9.8pt; white-space: pre;"><span style="color: #cf8e6d;">public static </span>String <span style="color: #56a8f5;">workDays</span>(<span style="color: #cf8e6d;">int </span>value) {<br /> <span style="color: #cf8e6d;">double</span>[] limits = {<span style="color: #2aacb8;">1</span>, <span style="color: #2aacb8;">2</span>, <span style="color: #2aacb8;">5</span>, <span style="color: #2aacb8;">6</span>};<br /> String[] formats = {<span style="color: #6aab73;">"Monday Blues"</span>, <span style="color: #6aab73;">"WorkHard"</span>, <span style="color: #6aab73;">"It is Friday!!"</span>, <span style="color: #6aab73;">"Relax"</span>};<br /><br /> ChoiceFormat form = <span style="color: #cf8e6d;">new </span>ChoiceFormat(limits, formats);<br /> <span style="color: #cf8e6d;">return </span>form.format(value);<br />}<br /><br /></div><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;"><span style="font-family: arial;"><br /></span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;"><span style="font-family: arial;"><br /></span></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjaCYI9973uYtCB9GYDDbULLJvCz8J3AXrDUahASnUpdvPrFtOPH-PoIx4rNb7MVJRYAL9_O_dIBP50GIGtG3L0lHZ23UduCfkSH_1cufGBb5q8g8z8-7iGAuP0QN5sZ5ZDArCzRx41pJ9Mnv7FQT6re1m7M4H6DjRPbUJi5qZ1FcrtWUMTXl_DTwgrSQVO" style="font-size: 9.8pt; margin-left: 1em; margin-right: 1em; text-align: center;"><img alt="" data-original-height="517" data-original-width="448" height="640" src="https://blogger.googleusercontent.com/img/a/AVvXsEjaCYI9973uYtCB9GYDDbULLJvCz8J3AXrDUahASnUpdvPrFtOPH-PoIx4rNb7MVJRYAL9_O_dIBP50GIGtG3L0lHZ23UduCfkSH_1cufGBb5q8g8z8-7iGAuP0QN5sZ5ZDArCzRx41pJ9Mnv7FQT6re1m7M4H6DjRPbUJi5qZ1FcrtWUMTXl_DTwgrSQVO=w555-h640" width="555" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: arial;">This should give fair bit of idea that this class will be useful in many places.</span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: arial;"><br /></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: arial;">Lets look at some application</span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: arial;"><br /></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: arial;"><b><i>- Better log messages</i></b></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: arial;"><br /></span></div><div class="separator" style="clear: both; text-align: left;"><div style="background-color: #1e1f22; color: #bcbec4; font-family: "JetBrains Mono", monospace; font-size: 9.8pt; white-space: pre;"><span style="color: #cf8e6d;">public static </span>String <span style="color: #56a8f5;">files</span>(<span style="color: #cf8e6d;">int </span>value) {<br /> <span style="color: #cf8e6d;">double</span>[] limits = {<span style="color: #2aacb8;">0</span>, <span style="color: #2aacb8;">1</span>, <span style="color: #2aacb8;">2</span>};<br /> String[] formats = {<span style="color: #6aab73;">"No files"</span>, <span style="color: #6aab73;">"One files"</span>, <span style="color: #6aab73;">"Many files"</span>};<br /><br /> ChoiceFormat form = <span style="color: #cf8e6d;">new </span>ChoiceFormat(limits, formats);<br /> <span style="color: #cf8e6d;">return </span>form.format(value);<br />}</div></div><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;"> </p></div><div><span style="font-family: courier;"> </span></div><div style="background-color: #1e1f22; color: #bcbec4; font-family: "JetBrains Mono", monospace; font-size: 9.8pt; white-space: pre;"><br />System.<span style="color: #c77dbb; font-style: italic;">out</span>.println(<span style="color: #6aab73;">"Found " </span>+ <span style="font-style: italic;">files</span>(<span style="color: #2aacb8;">100</span>)); <span style="color: #7a7e85;">//Found Many files<br /></span>System.<span style="color: #c77dbb; font-style: italic;">out</span>.println(<span style="color: #6aab73;">"Found " </span>+ <span style="font-style: italic;">files</span>(<span style="color: #2aacb8;">0</span>));<span style="color: #7a7e85;">//Found No files<br /></span>System.<span style="color: #c77dbb; font-style: italic;">out</span>.println(<span style="color: #6aab73;">"Found " </span>+ <span style="font-style: italic;">files</span>(<span style="color: #2aacb8;">1</span>));<span style="color: #7a7e85;">//Found One files<br /></span></div><div><span style="font-family: courier;"><br /></span></div><div><span style="font-family: arial;"><b><i>- Conditional Log message </i></b></span></div><div><span style="font-family: courier;"><br /></span></div><div><div style="background-color: #1e1f22; color: #bcbec4; font-family: "JetBrains Mono", monospace; font-size: 9.8pt; white-space: pre;"><div style="font-size: 9.8pt;"><span style="color: #cf8e6d;">public static </span>String <span style="color: #56a8f5;">formatMessage</span>(String format, <span style="color: #cf8e6d;">int </span>value) {<br /> ChoiceFormat form = <span style="color: #cf8e6d;">new </span>ChoiceFormat(format);<br /> <span style="color: #cf8e6d;">return </span>form.format(value);<br />}</div></div></div><div><span style="font-family: courier;"><br /></span></div><div><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 0px 0px 1.25em; white-space-collapse: preserve;"><br /></p><div style="background-color: #1e1f22; color: #bcbec4; font-family: "JetBrains Mono", monospace; font-size: 9.8pt; white-space: pre;"><br />String format = <span style="color: #6aab73;">"0#no files | 1#one file |2# two files |3< more than 2 "</span>;<br />System.<span style="color: #c77dbb; font-style: italic;">out</span>.println(<span style="font-style: italic;">formatMessage</span>(format, <span style="color: #2aacb8;">2</span>)); <span style="color: #7a7e85;">//two files<br /></span>System.<span style="color: #c77dbb; font-style: italic;">out</span>.println(<span style="font-style: italic;">formatMessage</span>(format, <span style="color: #2aacb8;">10</span>)); <span style="color: #7a7e85;">//more than 2<br /></span>System.<span style="color: #c77dbb; font-style: italic;">out</span>.println(<span style="font-style: italic;">formatMessage</span>(format, <span style="color: #2aacb8;">0</span>)); <span style="color: #7a7e85;">//no files<br /></span>System.<span style="color: #c77dbb; font-style: italic;">out</span>.println(<span style="font-style: italic;">formatMessage</span>(format, <span style="color: #2aacb8;">1</span>)); <span style="color: #7a7e85;">//one file<br /></span><span style="color: #7a7e85;"><br /></span></div><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 0px 0px 1.25em; white-space-collapse: preserve;"><br class="Apple-interchange-newline" />This example showcases the power of advanced string interpolation by utilizing rules embedded within the format string.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;">By leveraging this technique, we can define rules directly within the format string itself, which provides a flexible and concise approach to handle various scenarios.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 1.25em 0px 0px;"><i><b style="color: black; font-family: arial; font-size: medium; white-space-collapse: collapse;">- </b><span style="font-family: arial;"><b>Parameterised</b></span><b style="color: black; font-family: arial; font-size: medium; white-space-collapse: collapse;"> Conditional Log message </b></i></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 1.25em 0px 0px;"><span style="font-family: arial;">Multiple ways to do this, lets look at few example.</span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 1.25em 0px 0px;"><span style="font-family: arial;"><br /></span></p><div style="background-color: #1e1f22; color: #bcbec4; font-family: "JetBrains Mono", monospace; font-size: 9.8pt; white-space: pre;"><span style="color: #cf8e6d;">public static </span>ChoiceFormat <span style="color: #56a8f5;">usingPair</span>() {<br /> <span style="color: #cf8e6d;">double</span>[] priceLimits = {<span style="color: #2aacb8;">0.0</span>, <span style="color: #2aacb8;">10.0</span>, <span style="color: #2aacb8;">50.0</span>, <span style="color: #2aacb8;">100.0</span>};<br /> String[] priceFormats = {<br /> <span style="color: #6aab73;">"The item is not available"</span>,<br /> <span style="color: #6aab73;">"The item is on sale for {0}"</span>,<br /> <span style="color: #6aab73;">"The item is moderately priced at {0}"</span>,<br /> <span style="color: #6aab73;">"The item is expensive at {0}"<br /></span><span style="color: #6aab73;"> </span>};<br /> <span style="color: #cf8e6d;">return new </span>ChoiceFormat(priceLimits, priceFormats);<br />}</div><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 1.25em 0px 0px;"><i><b style="color: black; font-family: arial; font-size: medium; white-space-collapse: collapse;"><br /></b></i></p><div style="background-color: #1e1f22; color: #bcbec4; font-family: "JetBrains Mono", monospace; font-size: 9.8pt; white-space: pre;"><span style="color: #cf8e6d;">public static </span>ChoiceFormat <span style="color: #56a8f5;">usingStringLiteral</span>() {<br /> <span style="color: #cf8e6d;">return new </span>ChoiceFormat(<br /> <span style="color: #6aab73;">"0#The item is not available |10#The item is on sale for {0} |50#The item is moderately priced at {0} |100#The item is expensive at {0}"</span>);<br />}</div><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 1.25em 0px 0px;"><i><b style="color: black; font-family: arial; font-size: medium; white-space-collapse: collapse;"><br /></b></i></p><div style="background-color: #1e1f22; color: #bcbec4; font-family: "JetBrains Mono", monospace; font-size: 9.8pt; white-space: pre;"><span style="color: #cf8e6d;">public static </span>ChoiceFormat <span style="color: #56a8f5;">usingStringRules</span>() {<br /><br /> String rules = String.<span style="font-style: italic;">join</span>(<span style="color: #6aab73;">" |"</span>,<br /> <span style="color: #6aab73;">"0#The item is not available"</span>,<br /> <span style="color: #6aab73;">"10#The item is on sale for {0}"</span>,<br /> <span style="color: #6aab73;">"50#The item is moderately priced at {0}"</span>,<br /> <span style="color: #6aab73;">"100#The item is expensive at {0}"</span>);<br /><br /> <span style="color: #cf8e6d;">return new </span>ChoiceFormat(rules);<br />}</div><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;"><br /></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;"><span style="font-family: arial;">ChoiceFormat can be created using any of the methods mentioned above, each with its own advantages and considerations. However, some methods may be easier to maintain and less error-prone than others.</span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;"><span style="font-family: arial;">Among the options, if I were to choose one, I would prefer using the last method demonstrated, which involves using string rules. This method provides greater flexibility and simplicity in defining the rules for the ChoiceFormat. By using string rules, you can easily specify the mappings between input values and their corresponding text representations in a concise and readable manner. This approach often results in code that is easier to understand, modify, and maintain.</span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;"><span style="font-family: arial;">Above format can be used as below</span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;"><span style="font-family: arial;"><br /></span></p><div style="background-color: #1e1f22; color: #bcbec4; font-family: "JetBrains Mono", monospace; font-size: 9.8pt; white-space: pre;"><br />ChoiceFormat priceFormat = <span style="font-style: italic;">usingStringRules</span>();<br /><br /><span style="color: #cf8e6d;">double </span>price = <span style="color: #2aacb8;">120</span>;<br />Object[] formatArguments = {price};<br />String formattedPrice = MessageFormat.<span style="font-style: italic;">format</span>(priceFormat.format(price), formatArguments);<br />System.<span style="color: #c77dbb; font-style: italic;">out</span>.println(formattedPrice); <span style="color: #7a7e85;">// The item is expensive at 120<br /></span><span style="color: #7a7e85;"><br /></span></div><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;"><span style="font-family: arial;"><br /></span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;">Just imaging how this capability can be used by logging framework !</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;"><br /></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 0px 0px 1.25em; white-space-collapse: preserve;">ChoiceFormat empowers to amplify the range of choices available for message formatting. By incorporating ChoiceFormat into your code, you can introduce a multitude of options, enriching the formatting possibilities.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px; white-space-collapse: preserve;">The versatility of ChoiceFormat allows you to define and customize a wide array of choices, each with its own designated format. This flexibility enables you to create dynamic and adaptive messages that cater to different input values.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;">With ChoiceFormat at your disposal, you can enhance your message formatting capabilities, opening up new avenues for crafting comprehensive and adaptable output.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgba(69,89,164,.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 transparent; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 transparent; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 transparent; --tw-shadow: 0 0 transparent; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px 0px; white-space-collapse: preserve;"><br /></p></div><div><span style="font-family: courier;"><br /></span></div>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com0tag:blogger.com,1999:blog-6543397255913469784.post-74063322560342522052023-05-01T03:37:00.000-07:002023-05-01T03:37:16.710-07:00Distributed Context Propgation<h1 style="text-align: left;"><span style="background-color: #f7f7f8; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; white-space: pre-wrap;">Why distributed context ? </span></h1><span style="background-color: #f7f7f8; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; white-space: pre-wrap;">Nowadays, </span><span style="background-color: #f7f7f8;"><span style="color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif, Helvetica Neue, Arial, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;"><span style="white-space: pre-wrap;">modern applications are designed to be distributed across multiple data centers and regions, leveraging diverse technology stacks. As a result, a typical application architecture may span multiple geographical locations and incorporate various technologies, such as microservices, containers, and server less computing, to meet the complex demands of modern businesses</span></span></span>. <span style="color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif, Helvetica Neue, Arial, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;"><span style="background-color: #f7f7f8; white-space: pre-wrap;">Application stack might look something like this.</span></span><div> <div><br /></div><div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhaUyMX8xoDeJv0IlXzqXUyydVHRw2ZXD1t_WVIww9A2mxZ1dE0H4--4zqo-RV2COBRcYKFIsAH9VqsBS4FtERRb7_THdTtbTsyCkRjnTGnD7b2Yg3tFtZvEYjIb_UemfZtiU4FqeRxHBJoWkvrGQwF_KOgvqg9GcrEU5_GcAiFqY3IIa1hbq_PlNFE7Q" style="margin-left: auto; margin-right: auto;"><img alt="" data-original-height="1037" data-original-width="1577" height="421" src="https://blogger.googleusercontent.com/img/a/AVvXsEhaUyMX8xoDeJv0IlXzqXUyydVHRw2ZXD1t_WVIww9A2mxZ1dE0H4--4zqo-RV2COBRcYKFIsAH9VqsBS4FtERRb7_THdTtbTsyCkRjnTGnD7b2Yg3tFtZvEYjIb_UemfZtiU4FqeRxHBJoWkvrGQwF_KOgvqg9GcrEU5_GcAiFqY3IIa1hbq_PlNFE7Q=w640-h421" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Application Stack</td></tr></tbody></table><br /><br /></div><div><br /><div><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 0px 0px 1.25em; white-space: pre-wrap;">In distributed systems, several challenges arise when it comes to maintaining certain aspects such as <b>global state</b>, <b>telemetry recording</b>,<b> long-duration workflow pipeline</b> checkpointing, and <b>feature flags</b>. These challenges are critical to ensuring the reliability, scalability, and fault tolerance of the system.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px; white-space: pre-wrap;">One of the primary challenges is managing <b>global state</b>, which refers to the data that needs to be shared and synchronized across multiple instances of the application. This can be a complex task, especially in highly distributed environments, where consistency and concurrency control are crucial.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px; white-space: pre-wrap;">Another key challenge is recording <b>telemetry</b>, which involves collecting, analyzing, and visualizing data about the system's performance, health, and usage. This helps teams monitor and troubleshoot the system, identify bottlenecks, and optimize its overall efficiency.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px; white-space: pre-wrap;">In addition, keeping track of <b>long-duration workflow pipelines</b> is essential for ensuring that complex tasks are completed reliably and efficiently. This requires checkpointing and resuming workflows from a particular point in the event of failures or other disruptions.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px 0px; white-space: pre-wrap;">Finally, managing <b>feature flags,</b> which enable the selective enabling or disabling of certain features in the system, is critical for rapid iteration and experimentation. However, managing feature flags across multiple instances of the application can be challenging, as teams need to ensure that the correct flag configuration is propagated to all instances.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px 0px; white-space: pre-wrap;"><br /></p><h1 style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px 0px; text-align: left; white-space: pre-wrap;">What is solution ? </h1></div><div><br /></div><div><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 0px 0px 1.25em; white-space: pre-wrap;">Several solutions exist to tackle these challenges, such as distributed caching, message-oriented middleware, or specialized tools like Zookeeper for managing state. While these solutions can be effective, they often come with the complexity of managing a sophisticated infrastructure. Additionally, implementing a complex infrastructure for a relatively small use case can lead to more issues than solutions.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px; white-space: pre-wrap;">To address these challenges, OpenTelemetry offers a novel approach called "context and propagation" that operates at the transport layer. The idea behind this approach is to provide a simple and unified way to propagate information across different components of the distributed system.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px; white-space: pre-wrap;">Inspired by OpenTelemetry's context and propagation, I would like to introduce an idea that could help address these challenges more effectively. This approach involves utilizing a lightweight middleware layer that sits between the different components of the distributed system. The middleware layer would be responsible for managing the state, telemetry, and feature flags, and would provide a simple and standardized API for the application components to interact with.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px 0px; white-space: pre-wrap;">By using this approach, teams can simplify the management of complex infrastructure while ensuring that their distributed system remains reliable, scalable, and fault-tolerant. Additionally, this approach could help teams experiment with new features and iterate quickly without compromising on the overall performance and stability of the system.</p></div><div><br /></div><div><br /></div><div><h1><span style="background-color: #f7f7f8; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; white-space: pre-wrap;">How does context API look ? </span></h1></div><div><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 0px 0px 1.25em; white-space: pre-wrap;">The proposed Context API would have several key features to help manage the state, telemetry, and feature flags of a distributed system more effectively. Some of these features include:</p><ol style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; counter-reset: item 0; display: flex; flex-direction: column; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; list-style-image: initial; list-style-position: initial; margin: 1.25em 0px; padding: 0px 0px 0px 1rem; white-space: pre-wrap;"><li style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 0px; padding-left: 0.375em;"><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 0px;">Context Name: This feature would enable the association of a user-friendly name with all the state, telemetry, and feature flags in the system. This name can be used to index and query the information easily.</p></li><li style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 0px; padding-left: 0.375em;"><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 0px;">Time to Live: Some context information is short-lived and should be automatically managed by the underlying implementation. This feature would enable the setting of a time to live for the context information, allowing for automatic expiration and cleanup when no longer needed.</p></li><li style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 0px; padding-left: 0.375em;"><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 0px;">Abstract Data Types: The Context API would support abstract data types such as counters, maps, and sets, allowing for flexible and efficient management of the state, telemetry, and feature flags.</p></li><li style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 0px; padding-left: 0.375em;"><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 0px;">Durability: The context information would be durable, meaning it would survive application restarts and remain available for a longer duration, enabling teams to understand the behavior of the distributed system over time.</p></li></ol><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px 0px; white-space: pre-wrap;">By incorporating these features, the proposed Context API would provide a lightweight and standardized way to manage the complex state, telemetry, and feature flags of a distributed system, enabling teams to focus on building reliable and scalable applications without the burden of managing a complex infrastructure.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px 0px; white-space: pre-wrap;"><br /></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px 0px; white-space: pre-wrap;"><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEiiAn5oj5BVNp2j1UPWGPN3jjHOWTMlbsgQMRlhJdtO5X8L28IphmxaJ4VtnW9S1fMosrkFah9NsGNv6i6ySpWbwFKl0mdC8IVKISgmdYlYJ5UdKQOrR2N1bGvNSsgQJ08g6cJCDW7EICEz2qSAEBcXocT3IyzqhJku3GR2yySj_X8uNyh0nFugi359Hg" style="margin-left: auto; margin-right: auto;"><img alt="" data-original-height="769" data-original-width="1574" height="312" src="https://blogger.googleusercontent.com/img/a/AVvXsEiiAn5oj5BVNp2j1UPWGPN3jjHOWTMlbsgQMRlhJdtO5X8L28IphmxaJ4VtnW9S1fMosrkFah9NsGNv6i6ySpWbwFKl0mdC8IVKISgmdYlYJ5UdKQOrR2N1bGvNSsgQJ08g6cJCDW7EICEz2qSAEBcXocT3IyzqhJku3GR2yySj_X8uNyh0nFugi359Hg=w640-h312" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">API -> Implementation</td></tr></tbody></table><br /></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 0px 0px 1.25em; white-space: pre-wrap;">One of the key benefits of designing a system with a clear separation between the API and its implementation is the flexibility it offers. This approach allows teams to easily swap out and upgrade different components of the system without impacting the overall functionality of the API.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px; white-space: pre-wrap;">Moreover, a clear separation between the API and implementation enables the support of heterogeneous ecosystems, where different components of the system may use different technology stacks or operate in different environments. This allows teams to leverage the strengths of each technology stack and environment, without compromising the overall functionality and interoperability of the system.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px; white-space: pre-wrap;">In summary, a clear separation between the API and implementation provides teams with flexibility, interoperability, modularity, and maintainability, allowing them to build complex and reliable systems that can support the needs of diverse and dynamic ecosystems.</p><h1 style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px; text-align: left; white-space: pre-wrap;">What operations are supported on Abstract data type</h1><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 0px 0px 1.25em; white-space: pre-wrap;">Let's take a closer look at the sample API and the operations it supports. One of the benefits of the proposed Context API is its ability to support a wide range of use cases, and new abstract data types (ADTs) can be added to address more advanced scenarios.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px; white-space: pre-wrap;">At its core, the Context API provides a simple and standardized way to manage state, telemetry, and feature flags in a distributed system.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px; white-space: pre-wrap;">The Context API can be extended with new ADTs to support more advanced use cases. For example, a new ADT could be added to support distributed locks, allowing different components of the system to coordinate access to a shared resource. Another ADT could be added to support distributed queues, enabling components to communicate asynchronously in a reliable and scalable manner.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px; white-space: pre-wrap;"><br /></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px; white-space: pre-wrap;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhjWsmgsQkTvau_Bp8xASofBKvOUo6axwYmJl4H93NNmAEI3ezOYE8Fshj4ciwPqwdDtHFjdj_0_s-HFMWB2tWLV-gvRS5yT_Oev-wCD9XtO4KMrtRFMxkJ5asILhng9SB0GYcJHYehV6QqB-jp0APIj4cQvjhkL0Lc3TiCn_6MDNgsdn0HKd0vDQUsgg" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1015" data-original-width="1619" height="402" src="https://blogger.googleusercontent.com/img/a/AVvXsEhjWsmgsQkTvau_Bp8xASofBKvOUo6axwYmJl4H93NNmAEI3ezOYE8Fshj4ciwPqwdDtHFjdj_0_s-HFMWB2tWLV-gvRS5yT_Oev-wCD9XtO4KMrtRFMxkJ5asILhng9SB0GYcJHYehV6QqB-jp0APIj4cQvjhkL0Lc3TiCn_6MDNgsdn0HKd0vDQUsgg=w640-h402" width="640" /></a></div><br /><br /><p></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px; white-space: pre-wrap;">A sample implementation that leverages the concepts discussed above is available at <a href="https://github.com/ashkrit/corejava/tree/master/playground/src/main/java/context" style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; text-underline-offset: 2px;" target="_new">https://github.com/ashkrit/corejava/tree/master/playground/src/main/java/context</a>. This implementation showcases how the proposed Context API can be used to manage state, telemetry, and feature flags in a distributed system.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 0px 0px 1.25em; white-space: pre-wrap;">The sample implementation uses an H2 database for the persistence of state, and this database is abstracted through the ContextProviderClient.java interface. This abstraction enables the use of any backend system to store states, providing flexibility and adaptability.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px; white-space: pre-wrap;">One of the key challenges of the Context API is state replication in a distributed system. To address this, various approaches can be taken, depending on the requirements of the system. One option could be to use a REST API that has access to a replicated database system, where all state management is done through the API. Alternatively, a multi-data center cache, such as Redis, could be used to store and replicate state across different regions. In some cases, a distributed and replicated file system could also be used to store and manage state.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px 0px; white-space: pre-wrap;">Overall, the choice of backend system and replication strategy will depend on the specific needs of the distributed system and the desired level of fault tolerance, consistency, and scalability. The flexibility and abstraction provided by the Context API make it possible to use a variety of backend systems and replication strategies, enabling teams to choose the approach that best meets their requirements.</p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; margin: 1.25em 0px; white-space: pre-wrap;">Overall, the sample implementation provides a practical example of how the Context API can be used to simplify the management of state, telemetry, and feature flags in a distributed system, while promoting flexibility, scalability, and reliability.</p></div></div></div>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com0tag:blogger.com,1999:blog-6543397255913469784.post-34526371764371612442023-04-08T23:49:00.002-07:002023-04-08T23:49:14.971-07:00Safe refactoring using Scientist<p><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjqT66wubNZEvhkBoGDc9CSHWbyn5qM9N3LOZ9Oy6YSV34jE-OqrJqiI2kqP0drkzwAeCj1C_lluIsQxnE-gL1L2Umorn8NLZZ_qrt5QzzpxOnUYzcAEUkMlEF6owuhjAQGKRJfuZibijgq_j6H2PPrLYzUy6cVMkFUMwtqmV__hQW6q5vfbYmWLicYLQ" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="250" data-original-width="201" height="240" src="https://blogger.googleusercontent.com/img/a/AVvXsEjqT66wubNZEvhkBoGDc9CSHWbyn5qM9N3LOZ9Oy6YSV34jE-OqrJqiI2kqP0drkzwAeCj1C_lluIsQxnE-gL1L2Umorn8NLZZ_qrt5QzzpxOnUYzcAEUkMlEF6owuhjAQGKRJfuZibijgq_j6H2PPrLYzUy6cVMkFUMwtqmV__hQW6q5vfbYmWLicYLQ" width="193" /></a></span></div><span style="font-family: verdana;"><br /><br /></span><p></p><p><span style="font-family: verdana;">Refactoring is a critical yet often overlooked activity in the product development lifecycle. Despite its importance, teams tend to neglect it until they encounter significant showstoppers during the development process. Several factors contribute to teams neglecting refactoring, including.</span></p><p><span style="font-family: verdana;"><br /></span></p><span style="color: #374151; font-family: verdana;"><ul style="text-align: left;"><li><span style="color: #374151; font-family: verdana;"><span style="white-space: pre-wrap;">Pressure to meet product release dates
</span></span></li><li><span style="color: #374151; white-space: pre-wrap;">Concerns about production stability</span></li><li><span style="color: #374151; white-space: pre-wrap;">Difficulty in writing high-quality unit and functional tests</span></li></ul></span><p><span style="font-family: verdana;"><br /></span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 0px 0px 1.25em; white-space: pre-wrap;"><span style="font-family: verdana;">When teams encounter these roadblocks, they often make a business case for refactoring, reengineering, or implementing new technology. Although there may be resistance, the product team typically agrees to it, and it is integrated into the development sprints. If you see a specific tech debt item in the sprint backlog, it is a clear indication that the code's health was not maintained due to business priorities, and now it is time to address it.</span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px; white-space: pre-wrap;"><span style="font-family: verdana;">To put it in financial terms, failing to maintain code health is like missing an installment payment and incurring additional interest from your banker.</span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 1.25em 0px;"><span style="color: #374151; font-size: 16px; white-space: pre-wrap;"><span style="font-family: verdana;">Integrated Development Environments (IDEs) have evolved to provide safe and efficient refactoring options such as renaming variables, extracting methods, simplifying branch conditions, inlining methods, and moving code. While these options are generally beneficial, some refactoring tasks are more complex and riskier. These tasks may involve changing the implementation of core components, such as modifying persistence, altering core algorithms, or adjusting underlying data structures to improve performance.</span></span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 1.25em 0px;"><span style="color: #374151; font-family: verdana;"><span style="white-space: pre-wrap;">Some of the way to test such refactoring is by using feature flags or A/B testing.</span></span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 1.25em 0px;"><span style="color: #374151; font-size: 16px; white-space: pre-wrap;"><span style="font-family: verdana;">While browsing through GitHub, I came across <a href="https://github.com/github/scientist" target="_blank">Scientist</a>, a library that provides a way to verify critical refactoring. It offers an intuitive approach to code verification. It is based on experiment , observation & verification. </span></span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 1.25em 0px;"><br /></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 1.25em 0px;"><span style="color: #374151; font-family: Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 16px; white-space: pre-wrap;"></span></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh1C9HiykTG_qPQr8KQeyBdnq8sdAoFRWTIWvXBaCqpS3Kuelf5tT5GC_ocuRdoQJnB7yYIm3TcOEb1DGjLHpDYvhlORS3x-hI8acKb1Mt5l-6bL0UfO9S_BjsJNEfoIZV5N07U9tQtJFwxqN8Ksh66sAtk0ONmZWP5c3JNoP3MlDETP8k6OEWRfZkoeg" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="556" data-original-width="669" height="531" src="https://blogger.googleusercontent.com/img/a/AVvXsEh1C9HiykTG_qPQr8KQeyBdnq8sdAoFRWTIWvXBaCqpS3Kuelf5tT5GC_ocuRdoQJnB7yYIm3TcOEb1DGjLHpDYvhlORS3x-hI8acKb1Mt5l-6bL0UfO9S_BjsJNEfoIZV5N07U9tQtJFwxqN8Ksh66sAtk0ONmZWP5c3JNoP3MlDETP8k6OEWRfZkoeg=w640-h531" width="640" /></a></div><br /><br /><p></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 1.25em 0px;"><span style="font-family: verdana;"><span style="color: #374151; font-size: 16px; white-space: pre-wrap;">Let's take a look at some code snippets</span><span style="color: #374151; font-size: 16px; white-space: pre-wrap;">.</span></span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 1.25em 0px;"><span style="color: #374151; font-family: verdana;"><span style="white-space: pre-wrap;"><br /></span></span></p><div style="background-color: #1e1f22; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;"><pre>Experiment<Integer<span style="color: #cc7832;">, </span>Integer> experiment = <span style="color: #cc7832;">new </span>Experiment(<span style="color: #6a8759;">"Next Experiment"</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span>experiment<br /> .withControl(<span style="color: #6a8759;">"BitCount Using binary string"</span><span style="color: #cc7832;">, </span>x -><br /> (<span style="color: #cc7832;">int</span>) Integer.<span style="font-style: italic;">toBinaryString</span>(x)<br /> .chars()<br /> .filter(y -> y == <span style="color: #6a8759;">'1'</span>)<br /> .count()<br /> )<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span>experiment<br /> .withCandidate(<span style="color: #6a8759;">"BitCount using native"</span><span style="color: #cc7832;">, </span>x -> Integer.<span style="font-style: italic;">bitCount</span>(x))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span>experiment<br /> .withParamGenerator(() -> <span style="color: #6897bb;">100</span>)<br /> .compareResult(<span style="color: #6a8759;">"bit length"</span><span style="color: #cc7832;">, </span>(control<span style="color: #cc7832;">, </span>candidate) -> control == candidate)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span>experiment<br /> .run()<br /> .publish()<span style="color: #cc7832;">;</span></pre></div><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 0px 0px 1.25em; white-space: pre-wrap;"><span style="font-family: verdana;">This library has several components, including:</span></p><ul style="text-align: left;"><li><span style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space: pre-wrap;"><span style="font-family: verdana;">Control function</span></span></li><li><span style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space: pre-wrap;"><span style="font-family: verdana;">Candidate function</span></span></li><li><span style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space: pre-wrap;"><span style="font-family: verdana;">Experiment parameters</span></span></li><li><span style="background-color: #f7f7f8; color: #374151; font-size: 16px; white-space: pre-wrap;"><span style="font-family: verdana;">Result comparator function</span></span></li></ul><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px; white-space: pre-wrap;"><span style="font-family: verdana;">Once you specify these parameters, you can run experiments. As you begin to use the library for more complex problems, additional considerations may arise, such as the number of times the experiment should be run, whether to run them in parallel, and setting timeouts</span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 1.25em 0px;"></p><div><span style="color: #374151; font-family: verdana;"><span style="white-space: pre-wrap;"><br /></span></span></div><p></p><div style="background-color: #1e1f22; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;"><pre>experiment<br /> .withControl(<span style="color: #6a8759;">"BitCount Using binary string"</span><span style="color: #cc7832;">, </span>x -><br /> (<span style="color: #cc7832;">int</span>) Integer.<span style="font-style: italic;">toBinaryString</span>(x)<br /> .chars()<br /> .filter(y -> y == <span style="color: #6a8759;">'1'</span>)<br /> .count()<br /> )<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span>experiment<br /> .withCandidate(<span style="color: #6a8759;">"BitCount using native"</span><span style="color: #cc7832;">, </span>x -> Integer.<span style="font-style: italic;">bitCount</span>(x))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span>experiment<br /> .withParamGenerator(() -> <span style="color: #6897bb;">100</span>)<br /> .compareResult(<span style="color: #6a8759;">"bit length"</span><span style="color: #cc7832;">, </span>(control<span style="color: #cc7832;">, </span>candidate) -> control == candidate)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span>experiment<br /> .times(<span style="color: #6897bb;">100</span>)<br /> .parallel()<br /> .run()<br /> .publish()<span style="color: #cc7832;">;</span></pre></div><div></div><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 1.25em 0px;"></p><div><br /></div><p></p><h2 style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; margin: 1.25em 0px; text-align: left;"><span style="font-family: verdana;"><span style="color: #374151;"><span style="white-space: pre-wrap;">Other uses of Scientist</span></span></span></h2><div><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 0px 0px 1.25em; white-space: pre-wrap;"><span style="font-family: verdana;">Thus far, we have discussed this library's potential for safe refactoring, but it can also be utilized for running experiments alongside real production code. This allows for the experiment to be run under the same constraints as the current code and produce useful feedback.</span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px; white-space: pre-wrap;"><span style="font-family: verdana;">Since we are discussing experiments, it would be wise to store the results in a database or another system that can keep a log of the experiments.</span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px; white-space: pre-wrap;"><span style="font-family: verdana;">In many cases, running these experiments can be costly, so previous results can be utilized to verify new code.</span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px; white-space: pre-wrap;"><span style="font-family: verdana;">Furthermore, this library can be used to test multiple variations of new logic and select the most optimal one.</span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px; white-space: pre-wrap;"><span style="font-family: verdana;"><br /></span></p><p style="--tw-border-spacing-x: 0; --tw-border-spacing-y: 0; --tw-ring-color: rgb(59 130 246 / 0.5); --tw-ring-offset-color: #fff; --tw-ring-offset-shadow: 0 0 #0000; --tw-ring-offset-width: 0px; --tw-ring-shadow: 0 0 #0000; --tw-rotate: 0; --tw-scale-x: 1; --tw-scale-y: 1; --tw-scroll-snap-strictness: proximity; --tw-shadow-colored: 0 0 #0000; --tw-shadow: 0 0 #0000; --tw-skew-x: 0; --tw-skew-y: 0; --tw-translate-x: 0; --tw-translate-y: 0; background-color: #f7f7f8; border: 0px solid rgb(217, 217, 227); box-sizing: border-box; color: #374151; font-size: 16px; margin: 1.25em 0px; white-space: pre-wrap;"><span style="font-family: verdana;">Code used in this blog is available @ <a href="https://github.com/ashkrit/corejava/tree/master/scientist" target="_blank">github</a></span></p></div><div><span style="font-family: verdana;"><span style="color: #374151;"><span style="white-space: pre-wrap;"><br /></span></span></span></div><div><span style="font-family: verdana;"><span style="color: #374151;"><span style="white-space: pre-wrap;"><br /></span></span></span></div><div><br /></div><div><span style="font-family: verdana;"><span style="color: #374151;"><span style="white-space: pre-wrap;"><br /></span></span></span></div><div><span style="font-family: verdana;"><span style="color: #374151;"><span style="white-space: pre-wrap;"><br /></span></span></span></div><div><span style="font-family: verdana;"><span style="color: #374151;"><span style="white-space: pre-wrap;"><br /></span></span></span></div>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com0tag:blogger.com,1999:blog-6543397255913469784.post-19573162376652955662022-12-11T09:55:00.004-08:002022-12-11T09:55:58.195-08:00More on Dynamic proxy<div style="text-align: left;"><span style="font-family: verdana;">This is a follow up post from <a href="http://ashkrit.blogspot.com/2022/12/dynamic-proxy.html" target="_blank">dynamic proxy</a> to share about more realistic examples of dynamic proxy.</span></div><div style="text-align: left;"><span style="font-family: verdana;"><br /></span></div><div style="text-align: left;"><div style="text-align: left;"><span style="font-family: verdana;"><mark class="wordtune-highlight bg-info-lightest pt-[2px] pb-[3px]">Today's sof</mark>tware development is heavily reliant on system observability. Observability helps us to understand when the system degrades or misbehaves so that we can take proactive measures to fix it. </span></div></div><div style="text-align: left;"><span style="font-family: verdana;"><br /></span></div><div style="text-align: left;"><span style="font-family: verdana;"> </span></div><div style="text-align: left;"><span style="font-family: verdana;">Toy service for this example will be FXService </span></div><div style="text-align: left;"><span style="font-family: verdana;"><br /></span></div><div style="text-align: left;"><div style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;"><pre><span style="color: #cc7832;">public interface </span>FXService {<br /><br /> <span style="color: #cc7832;">double </span><span style="color: #ffc66d;">convert</span>(String from<span style="color: #cc7832;">, </span>String to<span style="color: #cc7832;">, int </span>amount)<span style="color: #cc7832;">;<br /></span>}</pre></div></div><div style="text-align: left;"><span style="font-family: verdana;"><br /></span></div><div style="text-align: left;"><span style="font-family: verdana;">This service will use <span style="background-color: white; color: #212121; font-size: 12px; white-space: pre-wrap;"><b><i>https://api.exchangerate.host</i></b> API for FX conversion.</span></span></div><div style="text-align: left;"><span style="font-family: verdana;"><span style="background-color: white; color: #212121; font-size: 12px; white-space: pre-wrap;"><br /></span></span></div><div style="text-align: left;"><span style="font-family: verdana;"><span style="background-color: white; color: #212121; font-size: 12px; white-space: pre-wrap;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgNduIp-rFOR9-zeRLEPxLyd1KB0b8vNOA4Ti6_vAlV3MQ1wphjl4yg7w96oeMvAf0gbxOVN71jSvp_Li8ydZr9S46A5hWlpKTHphU-61_jZOrKpkffIKf1-BnFa195bv0tgiG8kiQyiij3RmBZdDvFDOy0Um3XmmEKzDjkF2c3YIexoWHHrvqTTVwUVw" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="187" data-original-width="517" height="175" src="https://blogger.googleusercontent.com/img/a/AVvXsEgNduIp-rFOR9-zeRLEPxLyd1KB0b8vNOA4Ti6_vAlV3MQ1wphjl4yg7w96oeMvAf0gbxOVN71jSvp_Li8ydZr9S46A5hWlpKTHphU-61_jZOrKpkffIKf1-BnFa195bv0tgiG8kiQyiij3RmBZdDvFDOy0Um3XmmEKzDjkF2c3YIexoWHHrvqTTVwUVw=w493-h175" width="493" /></a></div><br /><br /></span></span></div><div style="text-align: left;"><span style="font-family: verdana;"><br /></span></div><div style="text-align: left;"><span style="font-family: verdana;">Lets look at what types of dynamic proxy we can create on top of this.</span></div><div style="text-align: left;"><span style="font-family: verdana;"><br /></span></div><h2 style="text-align: left;"><span style="font-family: verdana;">Method Timing</span></h2><div><p data-pm-slice="1 1 []" data-private="redact" data-wt-guid="26a51f81-9328-4b6f-82c4-a83f0180582a"></p><div style="text-align: left;"><span style="font-family: verdana;">It will keep track of method execution time and make it available later for analysis or other purposes. Using this proxy, X slow-running methods will be provided.<br />In FXService, there is only one method, so this proxy will create a method key with the method name and parameters.</span></div></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhyGvPAp2ipLTfXN1NUcNos1y0eFxOXNjoiGPHMSj_Fm_vc3K8PqHonlfDsjMCmSiEa8ooYovf6n27I9BRB9gBsqDB-y3Zx4VU1PtIV5L-WlYPERSLtG9AO8_SGZVKW8SqqmPlYiAaQzpqj2dAPdb_VvcYKms-IAEn8Rv3g96cTBEkXc3iba_O-ywd8og" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="160" data-original-width="714" height="224" src="https://blogger.googleusercontent.com/img/a/AVvXsEhyGvPAp2ipLTfXN1NUcNos1y0eFxOXNjoiGPHMSj_Fm_vc3K8PqHonlfDsjMCmSiEa8ooYovf6n27I9BRB9gBsqDB-y3Zx4VU1PtIV5L-WlYPERSLtG9AO8_SGZVKW8SqqmPlYiAaQzpqj2dAPdb_VvcYKms-IAEn8Rv3g96cTBEkXc3iba_O-ywd8og=w640-h224" width="640" /></a></div><br />Method timing proxy will show top X slow running method for eg. </span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana; font-size: x-small;"><br /></span></div><div><span style="font-family: verdana;"><div><b><i><span style="font-size: x-small;">Method convert( SGD,IDR,1 ) took 1041 ms</span></i></b></div><div><b><i><span style="font-size: x-small;">Method convert( SGD,GBP,1 ) took 994 ms</span></i></b></div><div><b><i><span style="font-size: x-small;">Method convert( SGD,USD,1 ) took 983 ms</span></i></b></div><div><b><i><span style="font-size: x-small;">Method convert( SGD,IDR,1 ) took 672 ms</span></i></b></div><div><b><i><span style="font-size: x-small;">Method convert( SGD,INR,1 ) took 650 ms</span></i></b></div><div><b><i><span style="font-size: x-small;">Method convert( SGD,USD,1 ) took 593 ms</span></i></b></div><div><b><i><span style="font-size: x-small;">Method convert( SGD,JPY,1 ) took 593 ms</span></i></b></div><div><b><i><span style="font-size: x-small;">Method convert( SGD,GBP,1 ) took 582 ms</span></i></b></div><div><b><i><span style="font-size: x-small;">Method convert( SGD,USD,1 ) took 580 ms</span></i></b></div><div><b><i><span style="font-size: x-small;">Method convert( SGD,INR,1 ) took 566 ms</span></i></b></div><div><br /></div><div>Such type of proxy is very helpful in identifying outage or degradation in API.</div><div><br /></div><div><h2 style="font-family: "Times New Roman";"><span style="font-family: verdana;">Stand In Processing</span></h2></div><div><p data-pm-slice="1 1 []" data-private="redact" data-wt-guid="c83b0fe9-2eff-4a2e-930d-5dc3456900d3">Proxy services such as this can be used to provide stand-in processing when the underlying service is down. For example, when a real FX service is down, this proxy can answer queries from the last successful call.</p></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhN9KbHa6eMW_7l653qr6DL89INPPpQq_t3s-k8nT-FV-lZbO6PT7CGMyq8njdJ8VGsVDXMKlxgVPyA7SwStSdeh6S7OVNzbsFfLYHijHhBuq-MvsURNqavrg3qACxWr5HsFvbKlXIAVcQ2j6hJ4JW695DU-ztvfA5ArwJFzsd17Y7hKWUkWaVLgN1C4g" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="201" data-original-width="730" height="176" src="https://blogger.googleusercontent.com/img/a/AVvXsEhN9KbHa6eMW_7l653qr6DL89INPPpQq_t3s-k8nT-FV-lZbO6PT7CGMyq8njdJ8VGsVDXMKlxgVPyA7SwStSdeh6S7OVNzbsFfLYHijHhBuq-MvsURNqavrg3qACxWr5HsFvbKlXIAVcQ2j6hJ4JW695DU-ztvfA5ArwJFzsd17Y7hKWUkWaVLgN1C4g=w640-h176" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><p data-pm-slice="1 1 []" data-private="redact" data-wt-guid="29b762cc-2339-4e9d-b77a-0b9b1839255e">It is useful for not only enhancing availability but also improving latency, since such a service can answer queries from the local cache right away. Additionally, it may be possible to save some costs if the underlying API is charged by usage.</p></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><h2 style="font-family: "Times New Roman";"><span style="font-family: verdana;">Chain of proxy</span></h2><div><span id="docs-internal-guid-f52316d7-7fff-b3a1-3efc-5eb1bb96b594"><span style="font-family: Verdana; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">Nice thing about proxies is that multiple proxies can be composed together to create a complex chain of proxy. For example, we can chain Stand In & Method timing together to get features of both.</span></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgs7Am6IRmwhYCrr6Rdo_r8yzIAM3spBurj9R_erz2D373Dpha9IuQCk57WKKlmRthoVp965rmkg6tjra97dLMLOLIAIsIsC9BsDFvP2ZA3PeAU-XLe5GiT1f8sD176b9uVBmxl3mMnK_lnoF-iIIHad7HveS7v4FlAVD00KJKJQBcIWYWBVId2JfG3VA" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="211" data-original-width="892" height="152" src="https://blogger.googleusercontent.com/img/a/AVvXsEgs7Am6IRmwhYCrr6Rdo_r8yzIAM3spBurj9R_erz2D373Dpha9IuQCk57WKKlmRthoVp965rmkg6tjra97dLMLOLIAIsIsC9BsDFvP2ZA3PeAU-XLe5GiT1f8sD176b9uVBmxl3mMnK_lnoF-iIIHad7HveS7v4FlAVD00KJKJQBcIWYWBVId2JfG3VA=w640-h152" width="640" /></a></div><br /><br /></span></div></div><div class="separator" style="clear: both; text-align: left;">Below code snippet is creating chain of proxy</div><div class="separator" style="clear: both; text-align: left;"><br /></div></span><div style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;"><pre>FXService core = <span style="color: #cc7832;">new </span>FXServiceAPI(<span style="color: #6a8759;">"https://api.exchangerate.host"</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">1</span>)<span style="color: #cc7832;">;<br /></span>FXService timeRecorderProxy = <span style="font-style: italic;">create</span>(FXService.<span style="color: #cc7832;">class, new </span>TimeRecorderProxy(core<span style="color: #cc7832;">, </span>tracker))<span style="color: #cc7832;">;<br /></span>FXService standInProxy = <span style="font-style: italic;">create</span>(FXService.<span style="color: #cc7832;">class, new </span>StandInProcessingProxy(timeRecorderProxy<span style="color: #cc7832;">, </span>cache))<span style="color: #cc7832;">;<br /></span></pre></div><span style="font-family: verdana;"><div class="separator" style="clear: both; text-align: left;"><span style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;">FXService fx = standInProxy</span><span style="background-color: #2b2b2b; color: #cc7832; font-family: "JetBrains Mono", monospace; font-size: 9pt;">;</span> </div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Full code using all the proxy</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;"><pre>List<String> currency = <span style="color: #cc7832;">new </span>ArrayList<String>() {{<br /> add(<span style="color: #6a8759;">"USD"</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>add(<span style="color: #6a8759;">"INR"</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>add(<span style="color: #6a8759;">"GBP"</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>add(<span style="color: #6a8759;">"IDR"</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>add(<span style="color: #6a8759;">"JPY"</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>add(<span style="color: #6a8759;">"CAD"</span>)<span style="color: #cc7832;">;<br /></span>}}<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span>IntStream.<span style="font-style: italic;">range</span>(<span style="color: #6897bb;">0</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">100</span>).forEach($ -> {<br /> Collections.<span style="font-style: italic;">shuffle</span>(<span style="color: #b389c5;">currency</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: #b389c5;">currency</span>.parallelStream().forEach(code -> {<br /> <span style="color: #cc7832;">try </span>{<br /> Double d = <span style="color: #b389c5;">fx</span>.convert(<span style="color: #6a8759;">"SGD"</span><span style="color: #cc7832;">, </span>code<span style="color: #cc7832;">, </span><span style="color: #6897bb;">1</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>System.<span style="color: #9876aa; font-style: italic;">out</span>.println(d)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>} <span style="color: #cc7832;">catch </span>(Exception e) {<br /> System.<span style="color: #9876aa; font-style: italic;">out</span>.println(<span style="color: #6a8759;">"Failed for " </span>+ code)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> })<span style="color: #cc7832;">;<br /></span>})<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span>tracker.dumpSlowRequests(<span style="color: #6897bb;">10</span>)<span style="color: #cc7832;">;<br /></span>cache.prettyPrint()<span style="color: #cc7832;">;</span></pre></div></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><br /></span></div>Code used in this post is available @ <a href="https://github.com/ashkrit/corejava/tree/master/playground/src/main/java/proxy/fx" target="_blank">fx service</a></span></div><div><span style="font-family: verdana;"><br /></span></div><div><h2 style="font-family: "Times New Roman";"><span style="font-family: verdana;">Conculsion</span></h2></div><div><span id="docs-internal-guid-07323703-7fff-b3ad-3ad4-a0977b0a060d"><span style="font-family: Verdana; font-size: 11pt; font-variant-east-asian: normal; font-variant-numeric: normal; vertical-align: baseline; white-space: pre-wrap;">A dynamic proxy is a powerful tool that is part of Java's ecosystem. It can be a very useful tool for writers of libraries or frameworks, since a proxy's primary purpose is to extend the functionality of an underlying service/api. Therefore, special precautions must be taken to ensure that it does not negatively impact the underlying service. </span></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><br /> </span></div></span></div>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com4tag:blogger.com,1999:blog-6543397255913469784.post-70240239819144938022022-12-11T05:54:00.002-08:002022-12-11T05:59:30.205-08:00Dynamic Proxy<p><span style="font-family: verdana;">In software design, a proxy pattern is one of the popular GOF design patterns. </span></p><p><span style="font-family: verdana;">A proxy is a class functioning as an interface to something else, it could be an interface to some business logic, network, file, or anything else.</span></p><p><span style="font-family: verdana;">This can also be seen as a wrapper around something core or real, main goal of a proxy is to add abstraction to get something extra it could be logging, permission check, cache, metric collection, etc.</span></p><p><span style="font-family: verdana;">We interact with proxies in real life. Take a example of bank interaction via ATM</span></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEh6lM5gnS0U_1b4MAJPlZ4FMU5V0ljreosBiU318DX1jQBcbxvo0J3vC0RlxpGM_qO9I0FkOhIeExEXrhnbEosLbJ4d4nvK_nCm8s5af8Q8f74TcXjCihWLty2F_Q4YRxxpqvmEfCNlyTBZghxoqyXBIrJLd3XWk3y1nmJHX_xxxzddz7sAkbC7QxIkGg" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="582" data-original-width="1189" height="157" src="https://blogger.googleusercontent.com/img/a/AVvXsEh6lM5gnS0U_1b4MAJPlZ4FMU5V0ljreosBiU318DX1jQBcbxvo0J3vC0RlxpGM_qO9I0FkOhIeExEXrhnbEosLbJ4d4nvK_nCm8s5af8Q8f74TcXjCihWLty2F_Q4YRxxpqvmEfCNlyTBZghxoqyXBIrJLd3XWk3y1nmJHX_xxxzddz7sAkbC7QxIkGg" width="320" /></a></span></div><span style="font-family: verdana;"><br /></span><p></p><p><br /></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;">ATM acts like a proxy to the bank branch, it allows to do almost everything that can be done at a branch.</span></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;">In software we see many variations of a proxy, some of the examples are in IO API in java</span></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgTirkeTAgM9ptSUAcpCoJWtEr0ddFbm093GgaEOQhmijMLrZnU_yd-eZfWD8cYJqGyLTxyvOKuaa1dWkYDN572CxgFs_aJxgzzUy3QfyT2isqPv4AHQvtBTkO1J8GzHX9FrMseFr0-pdCBahKZR4AkcSu_GKavJKpI_jSXez5dMXOnV_Cnqzf9fSlmtg" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="375" data-original-width="381" height="293" src="https://blogger.googleusercontent.com/img/a/AVvXsEgTirkeTAgM9ptSUAcpCoJWtEr0ddFbm093GgaEOQhmijMLrZnU_yd-eZfWD8cYJqGyLTxyvOKuaa1dWkYDN572CxgFs_aJxgzzUy3QfyT2isqPv4AHQvtBTkO1J8GzHX9FrMseFr0-pdCBahKZR4AkcSu_GKavJKpI_jSXez5dMXOnV_Cnqzf9fSlmtg=w298-h293" width="298" /></a></span></div><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><br /></span></div><span style="font-family: verdana;"><br />Another variation is chain of responsibility. </span><p></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhEe92D4tPMTgzO7XBeynUmZTh3pqxU0ZA-4bJ2gpyh8O6AatJcgKxmaPpGXTql3GbJEJmFrJvljd5VupVeAiS98m7xBpR3cek78VvbhVUm3Py-sqXPVSU1lLwWQyjkZDGXVhx52ijyI0K4SlxkeISm6bSvptpJB5IYDpUNNq2WWEV1xrATKfOSK0LRVg" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="177" data-original-width="284" height="249" src="https://blogger.googleusercontent.com/img/a/AVvXsEhEe92D4tPMTgzO7XBeynUmZTh3pqxU0ZA-4bJ2gpyh8O6AatJcgKxmaPpGXTql3GbJEJmFrJvljd5VupVeAiS98m7xBpR3cek78VvbhVUm3Py-sqXPVSU1lLwWQyjkZDGXVhx52ijyI0K4SlxkeISm6bSvptpJB5IYDpUNNq2WWEV1xrATKfOSK0LRVg=w400-h249" width="400" /></a></span></div><span style="font-family: verdana;"><br /><br /></span><p></p><p><span style="font-family: verdana;">In java proxy can be of 2 types it can be static and dynamic, lets look at a static proxy example.</span></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana; font-size: large;">Static Proxy</span></p><p><span style="font-family: verdana;">We are building Big Collection that allows to store unlimited data, our big collection interface looks like</span></p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;"><span style="color: #cc7832;">public interface </span>BigCollection<<span style="color: #507874;">V</span>> {<br /> <span style="color: #cc7832;">void </span><span style="color: #ffc66d;">add</span>(<span style="color: #507874;">V </span>value)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> boolean </span><span style="color: #ffc66d;">exists</span>(<span style="color: #507874;">V </span>value)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> void </span><span style="color: #ffc66d;">forEach</span>(Consumer<<span style="color: #507874;">V</span>> c)<span style="color: #cc7832;">;<br /></span>}</pre><p><br /></p><p><span style="font-family: verdana;">Static proxy will have same interface as original interface and will manually delegate calls to real object, it will look something like below.</span></p><p><span style="font-family: verdana;"><br /></span></p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;"><span style="color: #cc7832;">public class </span>BigCollectionProxy<<span style="color: #507874;">V</span>> <span style="color: #cc7832;">implements </span>BigCollection<<span style="color: #507874;">V</span>> {<br /><br /> <span style="color: #cc7832;">private final </span>Supplier<BigCollection<<span style="color: #507874;">V</span>>> <span style="color: #9876aa;">supplier</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> private final </span>BigCollection<<span style="color: #507874;">V</span>> <span style="color: #9876aa;">realObject</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> public </span><span style="color: #ffc66d;">BigCollectionProxy</span>(Supplier<BigCollection<<span style="color: #507874;">V</span>>> supplier) {<br /> <span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">supplier </span>= supplier<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> this</span>.<span style="color: #9876aa;">realObject </span>= supplier.get()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #bbb529;">@Override<br /></span><span style="color: #bbb529;"> </span><span style="color: #cc7832;">public void </span><span style="color: #ffc66d;">add</span>(<span style="color: #507874;">V </span>value) {<br /> <span style="color: #9876aa;">realObject</span>.add(value)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #bbb529;">@Override<br /></span><span style="color: #bbb529;"> </span><span style="color: #cc7832;">public boolean </span><span style="color: #ffc66d;">exists</span>(<span style="color: #507874;">V </span>value) {<br /> <span style="color: #cc7832;">return </span><span style="color: #9876aa;">realObject</span>.exists(value)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #bbb529;">@Override<br /></span><span style="color: #bbb529;"> </span><span style="color: #cc7832;">public void </span><span style="color: #ffc66d;">forEach</span>(Consumer<<span style="color: #507874;">V</span>> c) {<br /> <span style="color: #9876aa;">realObject</span>.forEach(c)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br />}</pre><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;">Client API for using the proxy will look something like below </span></p><p><span style="font-family: verdana;"><br /></span></p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;">BigCollection<String> collection = <span style="color: #cc7832;">new </span>BigCollectionProxy<>(AwsCollection::<span style="color: #cc7832;">new</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span>collection.add(<span style="color: #6a8759;">"Value1"</span>)<span style="color: #cc7832;">;<br /></span>collection.add(<span style="color: #6a8759;">"Value2"</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span>collection.forEach(System.<span style="color: #9876aa; font-style: italic;">out</span>::println)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span>System.<span style="color: #9876aa; font-style: italic;">out</span>.println(<span style="color: #6a8759;">"Exists " </span>+ collection.exists(<span style="color: #6a8759;">"Value2"</span>))<span style="color: #cc7832;">;</span></pre><p><span style="font-family: verdana;">Static proxy is easy to implement but it has got few problems </span></p><p></p><ul style="text-align: left;"><li><span style="font-family: verdana;">Manual delegation is painful and very verbose.</span></li><li><span style="font-family: verdana;">Any changes in interface required proxy also to implement changes.</span></li><li><span style="font-family: verdana;">Special treatment to functions that are part of language ecosystem like equals, hashcode,getClass etc. </span></li><li><span style="font-family: verdana;">and as name suggest it is static, can't change the behavior at runtime.</span></li></ul><p></p><p><span style="font-family: verdana;">Dynamic proxy solves issue with static proxy, lets look at dynamic proxy.</span></p><p><span style="font-family: verdana; font-size: x-large;">Dynamic Proxy</span></p><p><span style="font-family: verdana;">Dynamic proxy creates proxy at runtime, it is very flexible and convenient.</span></p><p><span style="font-family: verdana;"> JDK has <a href="https://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html" target="_blank">dynamic proxy</a> API since 1.3 that allows to create dynamic proxy using very simple API</span></p><p><span style="font-family: verdana;"></span></p><pre style="background-color: white; color: #353833; font-size: 1.3em; margin-top: 0px;">Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
new Class[] { Foo.class },
handler);
</pre><div><span style="font-family: verdana;">Lets create dynamic proxy for BigCollection class.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;">(BigCollection<<span style="color: #507874;">V</span>>) Proxy.<span style="font-style: italic;">newProxyInstance</span>(BigCollection.<span style="color: #cc7832;">class</span>.getClassLoader()<span style="color: #cc7832;">,<br /></span><span style="color: #cc7832;"> new </span>Class<?>[]{BigCollection.<span style="color: #cc7832;">class</span>}<span style="color: #cc7832;">,<br /></span><span style="color: #cc7832;"> new </span>BigCollectionDynamicProxy(supplier.get()))<span style="color: #cc7832;">;</span></pre></div><div><br /></div><p><span style="font-family: verdana;">This proxy looks exactly like BigCollection implementation and can be passed around. This also does not have the verbosity of static/hand crafted proxy, full proxy looks something like below</span></p><p><span style="font-family: verdana;"><br /></span></p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;"><span style="color: #cc7832;">public class </span>BigCollectionDynamicProxy <span style="color: #cc7832;">implements </span>InvocationHandler {<br /> <span style="color: #cc7832;">private final </span>Object <span style="color: #9876aa;">realObject</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> public </span><span style="color: #ffc66d;">BigCollectionDynamicProxy</span>(Object realObject) {<br /> <span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">realObject </span>= realObject<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #bbb529;">@Override<br /></span><span style="color: #bbb529;"> </span><span style="color: #cc7832;">public </span>Object <span style="color: #ffc66d;">invoke</span>(Object proxy<span style="color: #cc7832;">, </span>Method method<span style="color: #cc7832;">, </span>Object[] args) <span style="color: #cc7832;">throws </span>Throwable {<br /> <span style="color: #cc7832;">return </span>method.invoke(<span style="color: #9876aa;">realObject</span><span style="color: #cc7832;">, </span>args)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #cc7832;">public static </span><<span style="color: #507874;">V</span>> BigCollection<<span style="color: #507874;">V</span>> <span style="color: #ffc66d;">create</span>(Supplier<BigCollection<<span style="color: #507874;">V</span>>> supplier) {<br /> <span style="color: #cc7832;">return </span>(BigCollection<<span style="color: #507874;">V</span>>) Proxy.<span style="font-style: italic;">newProxyInstance</span>(BigCollection.<span style="color: #cc7832;">class</span>.getClassLoader()<span style="color: #cc7832;">,<br /></span><span style="color: #cc7832;"> new </span>Class<?>[]{BigCollection.<span style="color: #cc7832;">class</span>}<span style="color: #cc7832;">,<br /></span><span style="color: #cc7832;"> new </span>BigCollectionDynamicProxy(supplier.get()))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /></pre><p><span style="font-family: verdana;"><span style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;">}</span> </span></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;">Java reflection makes it easy to delegate calls to underlying real object.</span></p><p><span style="font-family: verdana;"><br /></span></p><h2 style="text-align: left;"><span style="font-family: verdana;">Dynamic Proxy Use case</span></h2><div><span style="font-family: verdana;">Lets look at some use case where dynamic proxy will come handy.</span></div><div><span style="font-family: verdana;"><br /></span></div><h3 style="text-align: left;"><span style="font-family: verdana;"> - Timing of method execution</span></h3><div><span style="font-family: verdana;">Elapsed time calculation is one of the cross-cutting concerns and proxy comes in handy for such use cases without the need of adding time tracking code all over the place.</span></div><div><span style="font-family: verdana;"> </span></div><div><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;"><span style="color: #cc7832;">public </span>Object <span style="color: #ffc66d;">invoke</span>(Object proxy<span style="color: #cc7832;">, </span>Method method<span style="color: #cc7832;">, </span>Object[] args) <span style="color: #cc7832;">throws </span>Throwable {<br /> <span style="color: #cc7832;">long </span>start = System.<span style="font-style: italic;">nanoTime</span>()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> try </span>{<br /> <span style="color: #cc7832;">return </span>method.invoke(<span style="color: #9876aa;">realObject</span><span style="color: #cc7832;">, </span>args)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>} <span style="color: #cc7832;">finally </span>{<br /> <span style="color: #cc7832;">long </span>total = System.<span style="font-style: italic;">nanoTime</span>() - start<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>System.<span style="color: #9876aa; font-style: italic;">out</span>.println(String.<span style="font-style: italic;">format</span>(<span style="color: #6a8759;">"Function %s took %s nano seconds"</span><span style="color: #cc7832;">, </span>method.getName()<span style="color: #cc7832;">, </span>total))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br />}</pre></div><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;"></span></p><h3><span style="font-family: verdana;"> - Single thread execution</span></h3><div><span style="font-family: verdana;">Many time some use case need single thread access to critical data structure. Dynamic proxy can add synchronization at the higher level, core code is not worrying about language level synchronization APIs.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;"><span style="color: #cc7832;">public </span>Object <span style="color: #ffc66d;">invoke</span>(Object proxy<span style="color: #cc7832;">, </span>Method method<span style="color: #cc7832;">, </span>Object[] args) <span style="color: #cc7832;">throws </span>Throwable {<br /> <span style="color: #cc7832;">synchronized </span>(<span style="color: #9876aa;">realObject</span>) {<br /> <span style="color: #cc7832;">return </span>method.invoke(<span style="color: #9876aa;">realObject</span><span style="color: #cc7832;">, </span>args)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br />}</pre></div><h3><span style="font-family: verdana;"> - Asynchronous Execution.</span></h3><div><span style="font-family: verdana;">Asynchronous execution is one of the common techniques to get best out of cores of underlying machine. Java has made parallel computation very easy with Completable future, parallel streams etc. Newer JDK version will have support for fibers and that will add concept of light weight threads.</span></div><div><br /></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Dynamic proxy can be used to convert synchronous API to asynchronous with simple code. </span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Code snippet for async exeuction.</span></div><div><span style="font-family: verdana;"><br /></span></div><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;"><span style="color: #9876aa;">es</span>.submit(() -> {<br /> <span style="color: #cc7832;">try </span>{<br /> System.<span style="color: #9876aa; font-style: italic;">out</span>.println(<span style="color: #6a8759;">"Using thread " </span>+ Thread.<span style="font-style: italic;">currentThread</span>().getName())<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: #b389c5;">method</span>.invoke(<span style="color: #9876aa;">realObject</span><span style="color: #cc7832;">, </span><span style="color: #b389c5;">args</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>} <span style="color: #cc7832;">catch </span>(Exception e) {<br /> e.printStackTrace()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /></pre><div><span style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;">})</span><span style="color: #cc7832; font-family: "JetBrains Mono", monospace; font-size: 9pt;">;</span><span style="font-family: verdana;"> </span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Few things to understand for such type of scenario</span></div><div><span style="font-family: verdana;"> - Value is not return, client API has to use some mechanism like call back handler or reply API to get value.</span></div><div><span style="font-family: verdana;"> - Exception handling.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><h3 style="font-family: "Times New Roman";"><span style="font-family: verdana;"> - Logging.</span></h3><div><span style="font-family: verdana;">This is straight forward use case and very popular one. </span></div></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;"><span style="color: #cc7832;">public </span>Object <span style="color: #ffc66d;">invoke</span>(Object proxy<span style="color: #cc7832;">, </span>Method method<span style="color: #cc7832;">, </span>Object[] args) <span style="color: #cc7832;">throws </span>Throwable {<br /> <span style="color: #cc7832;">long </span>start = System.<span style="font-style: italic;">nanoTime</span>()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> try </span>{<br /> <span style="color: #cc7832;">return </span>method.invoke(<span style="color: #9876aa;">realObject</span><span style="color: #cc7832;">, </span>args)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>} <span style="color: #cc7832;">finally </span>{<br /> <span style="color: #cc7832;">long </span>total = System.<span style="font-style: italic;">nanoTime</span>() - start<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>System.<span style="color: #9876aa; font-style: italic;">out</span>.println(String.<span style="font-style: italic;">format</span>(<span style="color: #6a8759;">"Function %s took %s nano seconds"</span><span style="color: #cc7832;">, </span>method.getName()<span style="color: #cc7832;">, </span>total))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br />}</pre></span></div><h1 style="text-align: left;"><span style="font-family: verdana;">Dynamic Proxy Tradeoff</span></h1><div><span style="font-family: verdana;">Nothing comes for free, dynamic proxy has below tradeoff.</span></div><div><span style="font-family: verdana;"><br /></span></div><div style="text-align: left;"><span style="font-family: verdana;">- Reflection cost </span></div><div style="text-align: left;"><span style="font-family: verdana;">- Parameter box/unboxing</span></div><div style="text-align: left;"><span style="font-family: verdana;">- Array overhead for parameters.</span></div><div style="text-align: left;"><span style="font-family: verdana;"><br /></span></div><div style="text-align: left;"><span style="font-family: verdana;">Reflection related cost are not that much of issue in JDK8 onwards. I wrote about reflection in few post.</span></div><div style="text-align: left;"><span style="font-family: verdana;"><br /></span></div><div style="text-align: left;"><span style="font-family: verdana;"><a href="http://ashkrit.blogspot.com/2013/01/java-reflection-facts.html" target="_blank">Java Reflection Facts</a><br /></span></div><p><span style="font-family: verdana;"><a href="http://ashkrit.blogspot.com/2014/06/methodhandle-returns-back-in-java-8.html" target="_blank">Methodhandle returns back in java 8</a></span></p><p><span style="font-family: verdana;">Boxing & Unboxing overhead are really hard to resolve and this will be always overhead for dynamic proxy unless some code generation technique is used.</span></p><div style="text-align: left;"><span style="font-family: verdana;">Dynamic proxy can be used to build many useful things. In next part of this post i will have more advance usage of dynamic proxy.</span></div><p><br /></p><p><span style="font-family: verdana;">Code used in this post is available @ <a href="https://github.com/ashkrit/corejava/tree/master/playground/src/main/java/proxy" target="_blank">proxy</a> github project</span></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;"><br /></span></p>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com4tag:blogger.com,1999:blog-6543397255913469784.post-57525263013564314942022-03-15T02:18:00.001-07:002022-03-15T02:18:11.818-07:00Concurrent Heap data structure<p><span style="font-family: verdana;">Lets Heapify!!!</span></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgTnnQFEz69SePGOfawMKQiUoBOlAJEU37PPQWQo57hI8MFTLITIp1YYZ5NawMVoyjBhqSH9JlnZ1Xa9RTF-Pid7tV907eAfXJqAsVNhuNc5TuRcd_2Y5VrcEKoluD9R8IdHN1Kl_bYSG4ALdFPljnBJpv2us9h4JbftXs3gActTHRH90MCWHvdx1mdDg" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="137" data-original-width="367" height="119" src="https://blogger.googleusercontent.com/img/a/AVvXsEgTnnQFEz69SePGOfawMKQiUoBOlAJEU37PPQWQo57hI8MFTLITIp1YYZ5NawMVoyjBhqSH9JlnZ1Xa9RTF-Pid7tV907eAfXJqAsVNhuNc5TuRcd_2Y5VrcEKoluD9R8IdHN1Kl_bYSG4ALdFPljnBJpv2us9h4JbftXs3gActTHRH90MCWHvdx1mdDg" width="320" /></a></span></div><span style="font-family: verdana;"><br /><br /></span><p></p><p><span style="font-family: verdana;">Heap is very popular data structure used for solving Top X types of problem.</span></p><p><span style="font-family: verdana;">For eg find the top 10 popular items by sales volume, top X users by activity etc.</span></p><p><span style="font-family: verdana;"><a href="https://docs.oracle.com/javase/7/docs/api/java/util/PriorityQueue.html" target="_blank">PriorityQueue</a> data structure of java is based on heap and can help in answering any top X type of query. </span><a href="https://docs.oracle.com/javase/7/docs/api/java/util/PriorityQueue.html" style="font-family: verdana;" target="_blank">PriorityQueue</a><span style="font-family: verdana;"> is not thread safe, so it can't be used in highly concurrent environment without adding lock.</span></p><p><span style="font-family: verdana;">Underlying data structure of heap is array and elements are shifted up and down to maintain the element order, for each swim operation the full array should be locked down to avoid race condition. </span></p><p><span style="font-family: verdana;">Even read options like poll are mutating operation due to which it is hard to share Heap with multiple threads.</span></p><p><span style="font-family: verdana;">Underlying algorithm makes is very hard to use heap in concurrent or parallel environment. </span></p><p><span style="font-family: verdana;"></span></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjk6IEzerGKCQX2vLI5eVon_S6L6apFMfuwcho_YNJnux9DmapkqbXXDZORocqib7_r4CzHVv2HoNawkriGcPRtLigZ4wr9iYb2AFf6QXz1ClPl_Zt6AkKnwKe1tnCwlxhjf1BKX3yyqX7gRo1eVlOE0aqFAftWvN4dEx8YFtvSHgAiXohjTJgXfpz_Eg" style="margin-left: auto; margin-right: auto;"><img alt="" data-original-height="1764" data-original-width="1582" height="400" src="https://blogger.googleusercontent.com/img/a/AVvXsEjk6IEzerGKCQX2vLI5eVon_S6L6apFMfuwcho_YNJnux9DmapkqbXXDZORocqib7_r4CzHVv2HoNawkriGcPRtLigZ4wr9iYb2AFf6QXz1ClPl_Zt6AkKnwKe1tnCwlxhjf1BKX3yyqX7gRo1eVlOE0aqFAftWvN4dEx8YFtvSHgAiXohjTJgXfpz_Eg=w358-h400" width="358" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Heap - Source: Wikipedia</td></tr></tbody></table><span style="font-family: verdana;"><br />Lets look at other options to achieve heap like functionality without giving up on concurrency.</span><div><p><span style="font-family: verdana;">Concurrent heap data structure need following properties</span></p><p></p><ul style="text-align: left;"><li><span style="font-family: verdana;">Highly concurrent ordered collection. </span></li><li><span style="font-family: verdana;">Parallel writes/read support.</span></li><li><span style="font-family: verdana;">Top X type of API.</span></li><li><span style="font-family: verdana;">Multiple top operations supported concurrently using same instance of data structure.</span></li></ul><p></p><p><span style="font-family: verdana;">Concurrent Skip list from JDK looks good candidate for this but we need to add some missing functionality.</span></p><p><span style="font-family: verdana;">Lets recap how Skip List data structure looks.</span></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;"></span></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEglNp7bZUjTYdz4HusLEmzh13kbM6gkDERUO42L8Sb4pg_o062XUAQUkQb2fcC20iJz_ZSLl8f3MsvZ7MVYbA7lqWX7vL0_qs7bGVk4z74W5EyLhJHFY6oK1wr0hHbu_mplnkHW-9BGSrlNzu0XICxbNuQHEeg4EMu1V1BgzcXHGuWi20xMgjhmNSYEwg" style="margin-left: auto; margin-right: auto;"><img alt="" data-original-height="110" data-original-width="470" height="94" src="https://blogger.googleusercontent.com/img/a/AVvXsEglNp7bZUjTYdz4HusLEmzh13kbM6gkDERUO42L8Sb4pg_o062XUAQUkQb2fcC20iJz_ZSLl8f3MsvZ7MVYbA7lqWX7vL0_qs7bGVk4z74W5EyLhJHFY6oK1wr0hHbu_mplnkHW-9BGSrlNzu0XICxbNuQHEeg4EMu1V1BgzcXHGuWi20xMgjhmNSYEwg=w400-h94" width="400" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">SkipList - Source:Wikipedia</td></tr></tbody></table><span style="font-family: verdana;"><br />SkipList is ordered multiple link list, it has got some fast lanes and slow lanes. Fast lanes allow to find element in approx log(n) cost.</span></div><div><br /></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><b>- Unique Item identification </b></span></div><div><span style="font-family: verdana;"><b><br /></b></span></div><div><span style="font-family: verdana;">JDK has set and map implementation of Skiplist. Map/Set can only have unique keys, we need to find a way to tweak unique key requirement to make set behave like Heap. </span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">We can use little trick, every value that is added to SkipList will have <b><i>additional metadata</i></b> that can <i><b>running sequence number</b></i> or timestamp, this extra metadata can be used for resolving conflict when 2 items are equal based on comparison.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Lets take product by sales use case for code samples. <br /><br />SalesItem is Comparable and it compares items by sales volume. </span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;"><span style="color: #cc7832;">class </span>SalesItem <span style="color: #cc7832;">implements </span>Comparable<SalesItem> {<br /><br /> <span style="color: #cc7832;">private final </span>String <span style="color: #9876aa;">product</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> private final long </span><span style="color: #9876aa;">sales</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span><span style="color: #bbb529;">@Override<br /></span><span style="color: #bbb529;"> </span><span style="color: #cc7832;">public int </span><span style="color: #ffc66d;">compareTo</span>(SalesItem o) {<br /> <span style="color: #cc7832;">return </span>Long.<span style="font-style: italic;">compare</span>(<span style="color: #9876aa;">sales</span><span style="color: #cc7832;">, </span>o.<span style="color: #9876aa;">sales</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}</pre><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;">}</pre><br /></span><p></p><p><span style="font-family: verdana;">We can't add SalesItem in SkipList because items having same sales volume will be rejected.</span></p><p><span style="font-family: verdana;">We can add another wrapper class that adds extra metadata to handle this problem. It will look something like this</span></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;"></span></p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;"><span style="color: #cc7832; font-size: 9pt;">class </span>Item <span style="color: #cc7832;">implements </span>Comparable<Item> {<br /> <span style="color: #cc7832;">private final </span><span style="color: #507874;">T </span><span style="color: #9876aa;">value</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> private final long </span><span style="color: #9876aa;">index</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span><span style="color: #bbb529;">@Override<br /></span><span style="color: #bbb529;"> </span><span style="color: #cc7832;">public int </span><span style="color: #ffc66d;">compareTo</span>(Item o) {<br /><br /> <span style="color: #cc7832;">int </span>r = <span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">value</span>.compareTo(o.<span style="color: #9876aa;">value</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>r = <span style="color: #9876aa;">heapType</span>.equals(HeapType.<span style="color: #9876aa; font-style: italic;">Max</span>) ? -r : r<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> if </span>(r != <span style="color: #6897bb;">0</span>) {<br /> <span style="color: #cc7832;">return </span>r<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> <span style="color: #cc7832;">return </span>Long.<span style="font-style: italic;">compare</span>(<span style="color: #9876aa;">index</span><span style="color: #cc7832;">, </span>o.<span style="color: #9876aa;">index</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}</pre><p><span style="font-family: verdana;"><b>index </b>is that extra metadata that is added to handle items with same sales volume and it case of conflict it will order by index</span></p><p><span style="font-family: verdana;"><b> - TopX API</b></span></p><p><span style="font-family: verdana;">For TopX API streams.limit can be used, another benifit of using streams APIs is that client application can use other cool features of Streams API.</span></p><p><span style="font-family: verdana;">Full Code for Concurrent Heap</span></p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;"><span style="color: #cc7832;">public class </span>ConcurrentHeap<<span style="color: #507874;">T </span><span style="color: #cc7832;">extends </span>Comparable> {<br /><br /> <span style="color: #cc7832;">private final </span>AtomicLong <span style="color: #9876aa;">id </span>= <span style="color: #cc7832;">new </span>AtomicLong()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> private final </span>NavigableSet<Item> <span style="color: #9876aa;">data </span>= <span style="color: #cc7832;">new </span>ConcurrentSkipListSet<>()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> private final </span>HeapType <span style="color: #9876aa;">heapType</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> public void </span><span style="color: #ffc66d;">add</span>(<span style="color: #507874;">T </span>value) {<br /> <span style="color: #9876aa;">data</span>.add(<span style="color: #cc7832;">new </span>Item(value<span style="color: #cc7832;">, </span><span style="color: #9876aa;">id</span>.incrementAndGet()))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #cc7832;">public </span>Stream<<span style="color: #507874;">T</span>> <span style="color: #ffc66d;">stream</span>() {<br /> <span style="color: #cc7832;">return </span><span style="color: #9876aa;">data<br /></span><span style="color: #9876aa;"> </span>.stream()<br /> .map(v -> v.<span style="color: #9876aa;">value</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #cc7832;">public </span>Stream<<span style="color: #507874;">T</span>> <span style="color: #ffc66d;">top</span>(<span style="color: #cc7832;">int </span>x) {<br /> <span style="color: #cc7832;">return </span>stream().limit(x)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #cc7832;">class </span>Item <span style="color: #cc7832;">implements </span>Comparable<Item> {<br /> <span style="color: #cc7832;">private final </span><span style="color: #507874;">T </span><span style="color: #9876aa;">value</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> private final long </span><span style="color: #9876aa;">index</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span><span style="color: #bbb529;">@Override<br /></span><span style="color: #bbb529;"> </span><span style="color: #cc7832;">public int </span><span style="color: #ffc66d;">compareTo</span>(ConcurrentHeap<<span style="color: #507874;">T</span>>.Item o) {<br /><br /> <span style="color: #cc7832;">int </span>r = <span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">value</span>.compareTo(o.<span style="color: #9876aa;">value</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>r = <span style="color: #9876aa;">heapType</span>.equals(HeapType.<span style="color: #9876aa; font-style: italic;">Max</span>) ? -r : r<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> if </span>(r != <span style="color: #6897bb;">0</span>) {<br /> <span style="color: #cc7832;">return </span>r<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> <span style="color: #cc7832;">return </span>Long.<span style="font-style: italic;">compare</span>(<span style="color: #9876aa;">index</span><span style="color: #cc7832;">, </span>o.<span style="color: #9876aa;">index</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #ffc66d;">Item</span>(<span style="color: #507874;">T </span>value<span style="color: #cc7832;">, long </span>index) {<br /> <span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">value </span>= value<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> this</span>.<span style="color: #9876aa;">index </span>= index<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> }</pre><p><br /></p><p><span style="font-family: verdana;">Underlying data structure that is behaving like Heap is NavigableSet, JDK has 2 implementation if this first one is TreeSet and another one is ConcurrentListSkipSet. </span></p><p><span style="font-family: verdana;">We can choose between </span><span style="font-family: verdana;">TreeSet/</span><span style="font-family: verdana;">ConcurrentListSkipSet based on need to avoid the cost of concurrency in single thread env.</span></p><p><span style="font-family: verdana;"><b> </b></span></p><p><span style="font-family: verdana;">Full working code for this blog post is available @ <a href="https://github.com/ashkrit/corejava/tree/master/playground/src/main/java/heap" target="_blank">github </a></span></p></div>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com5tag:blogger.com,1999:blog-6543397255913469784.post-56778576808361964322021-05-07T21:43:00.000-07:002021-05-07T21:43:37.120-07:00What is Artificial Intelligence ? <p><span style="font-family: verdana;">This is a post from series on <a href="http://ashkrit.blogspot.com/2021/05/artificial-intelligence-and-machine.html" target="_blank">artificial intelligence and machine</a>. </span></p><p><span style="font-family: verdana;">In this post, we will try to understand what AI is and where machine learning fits in it.</span></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHSZDLSetlFx10RkuuIx3IMUvTKuDrVanOXtSSW5-YH8Hd2c4a6Nqx9CqaOsqA3hmrYbnfh1RO0F1AbetDNR2mLes6kQQ4ThnURpMtwoGLC9WmY-fnVw9f3vMwkOqehsaz-ykKZNQ6zTMw/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="177" data-original-width="284" height="199" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHSZDLSetlFx10RkuuIx3IMUvTKuDrVanOXtSSW5-YH8Hd2c4a6Nqx9CqaOsqA3hmrYbnfh1RO0F1AbetDNR2mLes6kQQ4ThnURpMtwoGLC9WmY-fnVw9f3vMwkOqehsaz-ykKZNQ6zTMw/" width="320" /></a></span></div><span style="font-family: verdana;"><br /><br /></span><p></p><p><span style="font-family: verdana;">As per Wikipedia Artificial Inteligence is </span></p><p><span style="font-family: verdana;"><i>Simulating any intellectual task.</i></span></p><p><span style="font-family: verdana;">It can be also seen as the industrial revolution to simulate the brain.</span></p><p><span style="font-family: verdana;">AI is a very broad field and it contains many subfields and it is important to understand what the full landscape looks like and focus on the core part that overlaps with almost every subfield of AI.</span></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkTNlkff1L81Qn0AxEJZsEAn5dO_k3lh7-882C7Iq3-RYqzrhWdNJNwAH3LOqJSZT8F_dokrcnUWnJyHAToJCUsKrdXDtGO5GcHdeQP8LiFGAJusqECTpfKAGHTtvoiCDIM9tfm2u4Bg3y/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="488" data-original-width="739" height="422" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkTNlkff1L81Qn0AxEJZsEAn5dO_k3lh7-882C7Iq3-RYqzrhWdNJNwAH3LOqJSZT8F_dokrcnUWnJyHAToJCUsKrdXDtGO5GcHdeQP8LiFGAJusqECTpfKAGHTtvoiCDIM9tfm2u4Bg3y/w640-h422/image.png" width="640" /></a></span></div><span style="font-family: verdana;"><br /><br /></span><p></p><p><span style="font-family: verdana;">Let's try to understand each subfield.</span></p><h1 style="text-align: left;"><span style="font-family: verdana;">Knowledge representation</span></h1><div><span style="font-family: verdana;">This is core to many AI applications, it is based on an expert system that collects explicit knowledge that is available in some database or possessed by experts.</span></div><div><span style="font-family: verdana;">This can be also seen as Knowledge about knowledge. We interact with system type of system every day be it Amazon Alexa, Apple Siri, or Google Assistance.</span></div><div><span style="font-family: verdana;"> </span></div><div><h1><span style="font-family: verdana;">Perception</span></h1></div><div><span style="font-family: verdana;">Machine Perception is about using sensor input to understand context and action to take. Nowadays we are surrounded by cameras, microphones, IoT devices, etc.</span></div><div><br /></div><div><span style="font-family: verdana;">Some real-world applications include facial recognition, computer vision, speech, etc.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><h1><span style="font-family: verdana;">Motion and manipulation</span></h1></div><div><span style="font-family: verdana;">This is one of the heavy use of AI, it includes robotics. The industrial revolution has already helped the world economy grow, and robotics will take it to the next level. Some applications in industrial/domestic robots. In the time of pandemics like Covid, robotics is even going to help more as everyone is concerned about safety. Autonomous vehicles are one of the important applications of this sub-field. </span></div><div><br /></div><div><h1><span style="font-family: verdana;">Natural language processing</span></h1></div><div><span style="font-family: verdana;">NLP allows the machine to read and understand human language. It includes processing huge unstructured data and derives meaning from it. Some of the application that we get interact every day is search autocomplete, auto-correction, language translator, chatbots, targeted advertisement, etc.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><h1><span style="font-family: verdana;">Search and planning</span></h1></div><div><span style="font-family: verdana;">This area covers machine that is set a goal and achieves it. The machine builds the state of the world and can make predication on how their action will change it. </span></div><div><span style="font-family: verdana;"><br /></span></div><div><h1><span style="font-family: verdana;">Learning</span></h1></div><div><span style="font-family: verdana;">This is also called as Machine Learning and it is the study of computer algorithms that automatically improve through experience. </span></div><div><span style="font-family: verdana;">It sounds like how humans learn something!</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">It is a subfield of AI but the most important one as it is applied to all the subfields of AI, knowing this is a must before starting on any other subfield of AI.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiztGaele5pcBHxIY-NXRyrlZIuv5rn3W6SdDaTmJCcccFSHi3dvfkcEeoxX86KzxBVCvan5q98a_hhl88ElT-W3beD-z2Kvx5nco9hPTfq6BPHkAWiFqtbftkrG5O7ZRF1mLFmIcMtctL0/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="275" data-original-width="301" height="365" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiztGaele5pcBHxIY-NXRyrlZIuv5rn3W6SdDaTmJCcccFSHi3dvfkcEeoxX86KzxBVCvan5q98a_hhl88ElT-W3beD-z2Kvx5nco9hPTfq6BPHkAWiFqtbftkrG5O7ZRF1mLFmIcMtctL0/w400-h365/image.png" width="400" /></a></div><br /><br /></div><br /><br /></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Let's explore more on the Learning part now.</span></div><div><span style="font-family: verdana;"><br /></span></div><h2 style="text-align: left;"><span style="font-family: verdana;">What is machine learning? </span></h2><div><br /></div><div><span style="font-family: verdana;"> </span></div><div><span style="font-family: verdana;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbb027cvWSR21eG4OBQQa4Uo69llBDLsSs5PvC6OkoOFnRZijZ_9cZYeoKJ0gjImCfIG_TxpGTBgADzvIKmZkKaKUTX2FCpJynkkj3LZfqP196XIO363ab1HCstk2aPW2bfo1A3qjVASwv/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="341" data-original-width="369" height="369" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbb027cvWSR21eG4OBQQa4Uo69llBDLsSs5PvC6OkoOFnRZijZ_9cZYeoKJ0gjImCfIG_TxpGTBgADzvIKmZkKaKUTX2FCpJynkkj3LZfqP196XIO363ab1HCstk2aPW2bfo1A3qjVASwv/w400-h369/image.png" width="400" /></a></div><br /></span><span style="font-family: verdana;">One of the quick definitions of machine learning is pattern recognization, it can also be seen as how computers can discover to solve problems without explicit programming. </span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Machine learning is made up of 3 steps.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0_ot8ZM3wMaVG4LwPG9fe8J_CFTnQ3u43WctDAWcqHMzuU_fEqztwGWMewa_Qpm0jlpHuo0nkVd6hnXAk0uaLFPLJ_TtKZjtUWzaQKFrIwuYd8LKoov3HJV07XmDp3uv2wkzIlPBY-3Ar/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="237" data-original-width="599" height="254" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0_ot8ZM3wMaVG4LwPG9fe8J_CFTnQ3u43WctDAWcqHMzuU_fEqztwGWMewa_Qpm0jlpHuo0nkVd6hnXAk0uaLFPLJ_TtKZjtUWzaQKFrIwuYd8LKoov3HJV07XmDp3uv2wkzIlPBY-3Ar/w640-h254/image.png" width="640" /></a></div><br /><br /></span></div><div><span style="font-family: verdana;">The step of updating the model via learning is where real machine learning happens. </span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Data science is related to machine learning but is often seen as only machine learning. AI & data science good overlap with machine learning, it can be seen as below.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjGznXl4Y5GygW-paoSe7X5Hv61Bvo-dYJMrQnCvXjCB-Cige7QPNpjcc_o0mxLcEVI4m-hhn8LduXNT-CxV9tZe7VZttD5-C76qEj5ER-d3iCbMKhjnf3RMERZFW3Tf96Y_CpQ2PYHrSA/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="187" data-original-width="424" height="176" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjGznXl4Y5GygW-paoSe7X5Hv61Bvo-dYJMrQnCvXjCB-Cige7QPNpjcc_o0mxLcEVI4m-hhn8LduXNT-CxV9tZe7VZttD5-C76qEj5ER-d3iCbMKhjnf3RMERZFW3Tf96Y_CpQ2PYHrSA/w400-h176/image.png" width="400" /></a></div><div><br /></div><div><br /></div><span style="font-family: verdana;">Where does data science fits in machine learning? It is the unified concept of statistics, maths, data mining, data analysis, etc</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPdEs80PEaRChiP7YdJWQPxyCC8R1djOloh-8MDX2buIy7_PvYKVK0l543QsYX2ftIpf7RuzaPZrfvHrxPuAq77bmxZ29OX9VvaKICRAmDOyakuvzC8_ihToh_bLsl6CwlVLTqqGcdmpAj/" style="margin-left: auto; margin-right: auto;"><img alt="" data-original-height="370" data-original-width="391" height="378" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPdEs80PEaRChiP7YdJWQPxyCC8R1djOloh-8MDX2buIy7_PvYKVK0l543QsYX2ftIpf7RuzaPZrfvHrxPuAq77bmxZ29OX9VvaKICRAmDOyakuvzC8_ihToh_bLsl6CwlVLTqqGcdmpAj/w400-h378/image.png" width="400" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Data Science</td></tr></tbody></table><br /><br /></span><span style="font-family: verdana;">Now with a high-level understanding of AI, ML & data science, we are ready to do deep dive in ML.</span></div><div><br /></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><br /></span></div><div><br /></div>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com12tag:blogger.com,1999:blog-6543397255913469784.post-37832032922863072202021-05-07T21:41:00.003-07:002021-05-07T21:44:12.746-07:00Artificial Intelligence and machine learning <span style="font-family: verdana;">This post contains a catalog of high-level concepts in AI & ML.</span><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9hMwYUiJiae9cMfSbhtXzpIV_3Uc6m_iZr99hC6SHMKN5Fqfro91zz4VWYh5kbMAkqqWnuPbQkCRltMXOFko5fbwZWisicYEokDxPNTHBxKP5aMlIFsmFrP-fyB_fCfQs5mqF88EFl-dF/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="183" data-original-width="275" height="266" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9hMwYUiJiae9cMfSbhtXzpIV_3Uc6m_iZr99hC6SHMKN5Fqfro91zz4VWYh5kbMAkqqWnuPbQkCRltMXOFko5fbwZWisicYEokDxPNTHBxKP5aMlIFsmFrP-fyB_fCfQs5mqF88EFl-dF/w400-h266/image.png" width="400" /></a></div><br /><br /></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">I will keep on updating as I write more stuff</span></div><div><span style="font-family: verdana;"><br /></span></div><div><ul style="text-align: left;"><li><span style="font-family: verdana;"><a href="http://ashkrit.blogspot.com/2021/05/what-is-artificial-intelligence.html" target="_blank">What is Artificial Intelligence</a>? </span></li></ul><div><span style="font-family: verdana;"><br /></span></div></div>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com4tag:blogger.com,1999:blog-6543397255913469784.post-82850624931859568132021-04-10T22:48:00.002-07:002021-04-10T22:48:54.410-07:00Timeless investing lesson<p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">I usually don't share my thoughts about investment via the blog but thought it would be useful to record the most important investing lesson so that I can come back and refer to it.</span></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">The last few weeks have been interesting in the stocks investment world, some of the recent events reinforce timeless investment advice that people only learn by making mistakes.</span></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">In this post, I will share 2 such pieces of advice with very recent examples. </span></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"></span></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcX9ZRmMm9GcZFDS4TgbjCyjpP-GMmQI4Xd4E_Fxw4LhQwGiM9ZmxwYgYpmRsZCKRY0dwfXDv-rHzHePqyCMG8dp8i8f5BQPw4K-z0ND7J1d_T0hyphenhyphenYDmT5NyOvtWZO1d-A0TYFx9_e1fpu/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="168" data-original-width="300" height="224" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcX9ZRmMm9GcZFDS4TgbjCyjpP-GMmQI4Xd4E_Fxw4LhQwGiM9ZmxwYgYpmRsZCKRY0dwfXDv-rHzHePqyCMG8dp8i8f5BQPw4K-z0ND7J1d_T0hyphenhyphenYDmT5NyOvtWZO1d-A0TYFx9_e1fpu/w400-h224/image.png" width="400" /></a></span></div><span style="font-family: verdana;"><br /><br /></span><p></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><h1 style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; font-weight: normal; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">Never trade on leverage</span></span></h1><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">This is the number 1 reason why people lose money and also differentiate gambler(i.e trade) vs investor.</span></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><a href="https://www.cnbc.com/2021/03/29/credit-suisse-exits-positions-with-hedge-fund-warns-of-losses.html" target="_blank">Archegos hedge fund</a> took down 4 major investment banks on March 2021.</span></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">Let's try to understand leverage before we get into how Archegos crash and burned.</span></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">Meaning of leverage</span></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><strong style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"><em style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">"use borrowed capital for (an investment), expecting the profits made to be greater than the interest payable."</span></em></strong></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">The first exposure of leverage everyone gets exposed to is via housing loans, banks will fund part of the house price and the borrower will pay interest over the amount that is borrowed.</span></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">I would say leverage via house loan is good leverage because it allows to get shelter overhead and also has a good chance of price appreciation. </span></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">One of the most important things about a house loan is that tenor of payment is fixed and not linked to the underlying asset value. </span></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">If house price falls by 50% then the borrower doesn't have to pay more to a bank but he can gain if the price goes up by 50%. I am not saying a house loan is a good leverage but it is on the fence type of thing. </span></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">Lets see how leverage works in the investment world. </span></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;">In trading/investment world leverage is called a margin, the trading broker will allow x times of leverage to clients but with conditions that whenever they issue a margin call then the investor has to</span><strong style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"> deposit more money</strong><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"> or </span><strong style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;">sell some securities</strong><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;">.</span></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"> </span></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">Another example to understand this, assume we have 1 Million and we get 10X leverage, this means that we can buy securities worth of 10 million. </span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><br /></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_pyyamYGodQtH2Xb3q0b7zI8Jx3T-RVrOrDQW4mEqbzYXBWYVrfz8nPbdZM3Qui5oWTqlTzXXToorvkztuxa_wKqUFPnaJooiCY6Tl189BV4GW7vUcHB0OEi0AKfTlLCAc9z_el04I_7p/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="287" data-original-width="487" height="378" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_pyyamYGodQtH2Xb3q0b7zI8Jx3T-RVrOrDQW4mEqbzYXBWYVrfz8nPbdZM3Qui5oWTqlTzXXToorvkztuxa_wKqUFPnaJooiCY6Tl189BV4GW7vUcHB0OEi0AKfTlLCAc9z_el04I_7p/w640-h378/image.png" width="640" /></a></div><br /><br /><p></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">Only 2 things can happen and each has the probability of 50-50 in short term.</span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><h4 style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt; text-align: left;"><span style="font-family: verdana;">The bet goes your way</span></h4><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">This is the happy scenario where the market value of securities goes up by 20% and you are happy and also pay back some of the leverage and reduce risk.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><br /></span></div><div><h4 style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">The market is cruel to you</span></h4></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">This is what happens in most of the scenarios and you loose 20%, in this scenario, you lose your 1 Million and an additional 1 Million since you will not have the cash to fulfill the margin call you will start selling securities or take more leverage. In whatever options are selected it will cause panic in the market and will cause more selloff. </span></div><div><span style="font-family: verdana;"><br /></span></div><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><br /></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"><span style="color: #0e101a; font-family: verdana;">Now, this is exactly what happened with </span><a href="https://www.cnbc.com/2021/03/29/credit-suisse-exits-positions-with-hedge-fund-warns-of-losses.html" style="color: #0e101a; font-family: verdana;" target="_blank">Archegos hedge fund</a><span style="color: #0e101a; font-family: verdana;"> because a bet on one of the Chinese company did not go their way. </span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"></p><p style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; margin-bottom: 0pt; margin-top: 0pt;"></p><p></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">Archegos hedge fund is a family-run business and the owner has not so good reputation, they did few things very extreme to fail big.</span></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">- Took leverage of around 500 times.</span></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">- Used <a href="https://en.wikipedia.org/wiki/Contract_for_difference" target="_blank">Contract for difference</a>, which are very high leverage instruments and it is ban in many markets. Many individual investors are not allowed to trade in CFD by regulators. </span></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">Now how does anyone gets 500 times leverage? </span></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">They used a couple of brokers to get leverage and these are big names like Goldman Sachs, Morgon Stanley, Deutsche Bank, Credit Suisse, Nomura Holding.</span></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">None of these brokers were aware that Archegos is operating on such high margin and also to add that risk management process of these banks were not to the mark to issue some warning signal before it was too late.</span></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">Look at the stock prices of these brokers when this thing came out.</span></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"></span></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFycdyITvkFQDFv-HOzs_PRP5Xo0zhXECGqVCzxsEuVLNPH0CqDMqlV4dfssnP5lcCZJHTDLhyp05iWJkmrgXNZTQWWVP_sZRCh9jbE10_qcDr15vOTtGKy5OR_ZcJ8qB-a83yNCNXsq5p/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="601" data-original-width="1101" height="219" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFycdyITvkFQDFv-HOzs_PRP5Xo0zhXECGqVCzxsEuVLNPH0CqDMqlV4dfssnP5lcCZJHTDLhyp05iWJkmrgXNZTQWWVP_sZRCh9jbE10_qcDr15vOTtGKy5OR_ZcJ8qB-a83yNCNXsq5p/w400-h219/image.png" width="400" /></a></span></div><span style="font-family: verdana;"><br /><br /></span><p></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">Goldman Sachs & morgan stanley was little smart enough to recover their loss but Credit Suisse and Nomura were caught off guard.</span></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">Billions of dollars were wiped up, some of the numbers that are coming in news are <b>10 Billion</b> but many experts feel that it could be <b>100 Billion, </b>many of these banks have already declared that they are going to report losses in the next quarter and will also result in some people losing jobs.</span></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">As I write this post, the stock price has dropped more :-(</span></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">This is not the only example where a big leverage bet has gone wrong, the 2008 subprime crisis was also due to leverage and the common man was directly impacted by that.</span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">Keep eye on this news to understand the real impact.</span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">As an individual investor never trade on leverage. </span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><h1 style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt; text-align: left;"><span style="font-family: verdana;">Markets are efficient </span></h1><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">There are 2 popular styles of investing growth and value. <a href="https://en.wikipedia.org/wiki/Warren_Buffett" target="_blank">Warren Buffett</a> is a value investor and many companies and people try to follow him.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">It is hard to achieve anything close to Mr Warren because to become Warren you need the temperament and patience of warren. </span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Many investors try to <b><i>pick stocks</i></b> using value investing techniques and when they fail then they try to <b><i>pick a fund manager</i></b> that can do value investing for them.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Trust me that picking stock or fund management is like flipping a coin and the downside probability is very high. </span></div><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">On March 10, 2021, <a href="https://www.ivafunds.com/" target="_blank">International Value Advisers ( IVA)</a> decided to liquidate the fund.</span></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></span></p><p style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span data-preserver-spaces="true" style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"></span></span></p><div class="separator" style="clear: both; color: #0e101a; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXGqrSk0kiCMzT1eKbLjrggGVPnfBHJ7CnWWoe7ECG9WHuRgvUI8BdA3m2ly5GFsKxjL5zUcymgMR8SX1qZT26POx1yZHSVVBgcqlADdYsy8vYXKqIdu7Tn4gXsNlQD6Y_RPdIAnrsBIdL/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="590" data-original-width="1123" height="336" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXGqrSk0kiCMzT1eKbLjrggGVPnfBHJ7CnWWoe7ECG9WHuRgvUI8BdA3m2ly5GFsKxjL5zUcymgMR8SX1qZT26POx1yZHSVVBgcqlADdYsy8vYXKqIdu7Tn4gXsNlQD6Y_RPdIAnrsBIdL/w640-h336/image.png" width="640" /></a></span></div><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></span></span></p><p style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><span data-preserver-spaces="true" style="background: transparent; color: #0e101a; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></span></span></p><p style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><span data-preserver-spaces="true" style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><span style="color: #0e101a;">IVA was an esteem value shop and it has a sad and common end of actively managed funds.</span></span></span></span></p><p style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><span data-preserver-spaces="true" style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><span style="color: #0e101a;"><br /></span></span></span></span></p><p style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><span data-preserver-spaces="true" style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><span style="color: #0e101a;">IVA was sitting on cash for a very long time because they thought the market is expensive, there was a time when the fund had <b><i>50%</i></b> cash waiting to be deployed.</span></span></span></span></p><p style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><span data-preserver-spaces="true" style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><span style="color: #0e101a;"><br /></span></span></span></span></p><p style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><span data-preserver-spaces="true" style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><span style="color: #0e101a;">They got asset allocation wrong and waited for the <b><i>timing market</i></b>. </span></span></span></span></p><p style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><span data-preserver-spaces="true" style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><span style="color: #0e101a;">The efficient market hypothesis </span></span></span><span style="color: #0e101a;">states that share prices reflect all information and consistent alpha generation is impossible.</span></span></p><p style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><span style="color: #0e101a;"><br /></span></span></p><p style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><span style="color: #0e101a;">Every now and then someone will come and tell the market is inefficient and will try to fight against it.</span></span></p><p style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><span style="color: #0e101a;"><br /></span></span></p><p style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><span style="color: #0e101a;">This has been proved multiple times and a nice article was posted on Forbes about it, it is called <a href="https://www.forbes.com/sites/rickferri/2012/12/20/any-monkey-can-beat-the-market/?sh=7f72e3fb630a" target="_blank">any monkey can beat the market</a>.</span></span></p><p style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><span style="color: #0e101a;"><br /></span></span></p><p style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><span style="color: #0e101a;">IVA investors would have made lots of money by just investing in a broad market index fund. </span></span></p><p style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><a href="https://www.morningstar.com/articles/708457/behind-the-rise-and-fall-of-an-esteemed-value-shop" style="font-family: verdana;" target="_blank">Morningstar</a><span style="font-family: verdana;"> has done a nice analysis of the IVA fund, read this to understand in detail what went wrong. </span></p><p style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><p style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><p style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;">If you can't beat the market then be the market, Index fund should be the core strategy. </span></p><p style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><br /></p><p style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhR8ZhroDGyydfba-ewag_YSOORQ0fDkqxk5-pcTxLJytRUfCCcooBlW58PmU_qvVEfd6y4k_SjdMwCj4O8jOcPQsVGAuLoIO0-7XAh5L33lqvxW64W3-kPpYow0eUsNtpwdxkjQujY4K5V/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="283" data-original-width="310" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhR8ZhroDGyydfba-ewag_YSOORQ0fDkqxk5-pcTxLJytRUfCCcooBlW58PmU_qvVEfd6y4k_SjdMwCj4O8jOcPQsVGAuLoIO0-7XAh5L33lqvxW64W3-kPpYow0eUsNtpwdxkjQujY4K5V/" width="263" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><span style="font-family: verdana;">I will leave you with one more interesting read about <a href="https://www.nasdaq.com/articles/warren-buffett-just-won-10-year-million-dollar-bet-heres-why-2018-01-08" target="_blank">Warren Buffett Just Won a 10-Year Million-Dollar Bet</a>, where he challenges hedge fund managers to beat the market and they end up losing and have to close the fund.</span><p></p><p style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span></p><p style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4mq7C0DAms49Y1QrG9g5ZxWzNjURXPM1XZOY7NjujFHM0FbkcJwvug64R1WU7cfqJ6YqS6tkuq3kjwOrlRimNMGGmdJkL1w1OOFO8AX9FQ1ae23fOmzlWnyNQmkyVzyaSDXbhHpwe-02j/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="415" data-original-width="643" height="414" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4mq7C0DAms49Y1QrG9g5ZxWzNjURXPM1XZOY7NjujFHM0FbkcJwvug64R1WU7cfqJ6YqS6tkuq3kjwOrlRimNMGGmdJkL1w1OOFO8AX9FQ1ae23fOmzlWnyNQmkyVzyaSDXbhHpwe-02j/w640-h414/image.png" width="640" /></a></span></div><span style="font-family: verdana;"><br /><br /></span><p></p><p style="background: transparent; margin-bottom: 0pt; margin-top: 0pt;"><span style="font-family: verdana;"><br /></span><br /></p><span style="font-family: verdana;"><div><span style="font-family: verdana;"><br /></span></div>I will wrap up now but if you want to remember one thing then it has to be <b><i>"never trade on leverage"</i></b></span><div><span style="font-family: verdana;"><b><i><br /></i></b></span></div><div><span style="font-family: verdana;"><b><i><br /></i></b></span></div>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com7tag:blogger.com,1999:blog-6543397255913469784.post-64488284426835960442021-04-04T08:36:00.002-07:002021-04-04T08:36:27.357-07:00Data partitioning approach.<p><span style="font-family: verdana;">Partition is the technique of breaking a large data set into small manageable chunks.</span></p><p><span style="font-family: verdana;">It is important to define partition strategy such that a single piece of information belongs to a single partition only, good partition strategy enables data level parallelism.</span></p><p><span style="font-family: verdana;">Partition is not a new idea; it comes from the RDBMS world where it is used for creating partition indexes.</span></p><p><span style="font-family: verdana;">Atomicity between partitions, especially for writing, is hard to achieve and it requires some tradeoff.</span></p><p><span style="font-family: verdana;">Partition should not be confused with replication, replication is keeping multiple copies of a single partition for high availability or fault tolerance. </span></p><p><span style="font-family: verdana;">Partition + Replication makes any data system fully distributed. </span></p><p><span style="font-family: verdana;">In this post we will look at different approaches to data partitioning. Lets pick email address dataset as a sample data for partition example.</span></p><p><span style="font-family: verdana;">Let's take <i style="font-weight: bold;">bill.gates@microsoft.com </i>email as an example.</span></p><p><span style="font-family: verdana;">This email can be partitioned by hash(bill.gates@microsoft.com) or by range(</span><span style="font-family: verdana;">bill.gates@microsoft.com) based on how it is queried. </span></p><h2 style="text-align: left;"><span style="font-family: verdana;">Hash</span></h2><div><div><span style="font-family: verdana;">Hash one is simplest to understand but when we are in a distributed environment then it is not that simple because nodes that are storing data can go offline or new nodes are added to manage load. Due to dynamic node numbers we don't have a constant denominator to find mod for key.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">It is important to understand why hash table type of data structure can't be used for data partitioning in distributed systems.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Classic hash table starts with N capacity and based on load factor(i.e how much it is filled) it will resize and all the keys need to rehash based on new capacity.</span></div><div><span style="font-family: verdana;">This resize is an expensive operation and also during resize the hash table might not be available for some operations.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Resizing a distributed dataset will mean shuffling all the keys and when a dataset contains billions or trillion of keys then it is going to generate a huge load on the system and also making it unavailable for end users.</span></div><div><br /></div><p><span style="font-family: verdana;">If we look in terms of BIG O then classic hash table complexity will look something like below.</span></p></div><p style="text-align: left;"><span style="font-family: verdana;"><br /></span></p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT6IUbUKGKQd5u2yEkk_M4eb_Yckna1YfligoR72Dbe_QU_EFN1kRul8ILZP7TosChx72jzoXULcFNbZHJZFaw60YWuwsoekP2Id1zFU0iYgqHIP7Nq4v3egCjTtVN3MYk7EAJSAZOUuWr/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="335" data-original-width="575" height="372" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT6IUbUKGKQd5u2yEkk_M4eb_Yckna1YfligoR72Dbe_QU_EFN1kRul8ILZP7TosChx72jzoXULcFNbZHJZFaw60YWuwsoekP2Id1zFU0iYgqHIP7Nq4v3egCjTtVN3MYk7EAJSAZOUuWr/w640-h372/image.png" width="640" /></a></div><br /><p></p><p><span style="font-family: verdana;">It is very clear that the Add/Remove node option is going to bring everything to standstill. Some distributed systems like Kafka have fixed no of partition strategies to avoid exactly this problem. </span></p><div><span style="font-family: verdana;">So what can be done in this case ?</span></div><div><span style="font-family: verdana;">Key insight is using a range of hash codes to represent keys and it helps in reducing overhead of key migration when a new node is added or removed.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Sets up nicely for next section</span></div><h3 style="text-align: left;"><span style="font-family: verdana;">Consistent Hashing</span></h3><div><div><span style="font-family: verdana;">Consistent hashing is now a new idea but it is more than a 2 decade old idea that was mentioned in <a href="https://draft.blogger.com/blog/post/edit/6543397255913469784/6448828442683596044#">paper</a>.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">This is a special hash function which changes minimally as new nodes are added/removed. In this approach both keys and slots/nodes are hashed using the same function and enables mapping nodes to intervals of keys.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">All the nodes are placed in a ring and each node is responsible for range of hash. Due to this range hash property whenever a node is added or removed k/n keys need to be replaced where k is total number of keys and n is number of nodes or slots.</span></div></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjch6m5wPZoqeYmu1hobzGkDtsBjBxqQcEZnoZwLzEpAy5MXrYxEHeh5oUmwwyvzzz9squmxqdGwDTjLHx_73pmtQfk9fYxf6Eys1LFa9Zfh56aON9h9E5R_w9BaaI7nNuNgbMY0ShYuss5/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="360" data-original-width="537" height="430" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjch6m5wPZoqeYmu1hobzGkDtsBjBxqQcEZnoZwLzEpAy5MXrYxEHeh5oUmwwyvzzz9squmxqdGwDTjLHx_73pmtQfk9fYxf6Eys1LFa9Zfh56aON9h9E5R_w9BaaI7nNuNgbMY0ShYuss5/w640-h430/image.png" width="640" /></a></div><br /><br /></div>Each node is responsible for a range of keys due to which when a node is added or removed only part of keys needs to be replaced. Let's look at example </span></div><div><span style="font-family: verdana;"><br /></span></div><div><h3 style="text-align: left;"><span style="font-family: verdana;">Node removed</span></h3><div><span style="font-family: verdana;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-2OVNEbUSyMCH69pFr_1wZISTGHyRlcMkKL8zgHH7CBqXZb1xkK6EWejFKp7Pu_DL-QOe883zfGZ1otubylNSOTsG0lTxjsHgHatiq4Pz-Q1B89uXCoSw53hM2Fxrw5e8iPT7OGys371R/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="364" data-original-width="519" height="448" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-2OVNEbUSyMCH69pFr_1wZISTGHyRlcMkKL8zgHH7CBqXZb1xkK6EWejFKp7Pu_DL-QOe883zfGZ1otubylNSOTsG0lTxjsHgHatiq4Pz-Q1B89uXCoSw53hM2Fxrw5e8iPT7OGys371R/w640-h448/image.png" width="640" /></a></div><br /><br /></span></div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">In this example node B is lost and node A takes up responsibility and now handles range ( 1 to 200). Only keys between 101 to 200 need to move and this is much better than a naïve hash table.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><h3><span style="font-family: verdana;">Node Added</span></h3></div><div><span style="font-family: verdana;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsz-m-_bEGTHYEXrqToHJ6Xs5AW7FxTzsBFVtKHKaEPnzsLcZrtEXmooVDEK0sCVeNeLlFXmU1GBju0w3AzPyr1W2iI7ZSZshGzqdvVGsez1nLJtshdtD9RdY3E9YzDqz_vTsvpfCifyyU/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="350" data-original-width="507" height="442" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsz-m-_bEGTHYEXrqToHJ6Xs5AW7FxTzsBFVtKHKaEPnzsLcZrtEXmooVDEK0sCVeNeLlFXmU1GBju0w3AzPyr1W2iI7ZSZshGzqdvVGsez1nLJtshdtD9RdY3E9YzDqz_vTsvpfCifyyU/w640-h442/image.png" width="640" /></a></div><br />New node C.1 is added and it shares responsibility by handling part of keys from C.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Few things are done to balance load</span></div><h3 style="text-align: left;"><span style="font-family: verdana;"> Rebalancing by adjusting ranges.</span></h3><div><span style="font-family: verdana;">Under this approach the background process is checking load(i.e. no of keys) on nodes. Any node that is overloaded or underloaded is balanced. </span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><br /></span></div><h3 style="text-align: left;"><span style="font-family: verdana;">Add virtual nodes.</span></h3><div><span style="font-family: verdana;">One of the problem with consistent hash is that some nodes become overload and to manage that it uses concept of virtual nodes, which are replica of nodes. This adds more nodes/slots and improves probability of better load balancing. </span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">"Virtual Node" also gives one nice option to manage nodes with different capacity, if cluster contains node that is 2X bigger than other nodes then it can have more replica compared to others.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"> Lets look at one simple implementation. Code contains 2 components one is ConsistentHash and Distributed hash using consistent hash.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;"><span style="color: #cc7832;">public class </span>ConsistentHashing<<span style="color: #507874;">T</span>> {<br /><br /> <span style="color: #cc7832;">private final </span>Function<<span style="color: #cc7832;">byte</span>[]<span style="color: #cc7832;">, </span>Integer> <span style="color: #9876aa;">hashFunction</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> private final int </span><span style="color: #9876aa;">replica</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> private final </span>SortedMap<Integer<span style="color: #cc7832;">, </span><span style="color: #507874;">T</span>> <span style="color: #9876aa;">ring </span>= <span style="color: #cc7832;">new </span>TreeMap<>()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> private final </span>Function<<span style="color: #507874;">T</span><span style="color: #cc7832;">, </span>String> <span style="color: #9876aa;">nodeKey</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><br /> <span style="color: #cc7832;">public void </span><span style="color: #ffc66d;">add</span>(<span style="color: #507874;">T </span>node) {<br /> <span style="color: #cc7832;">for </span>(<span style="color: #cc7832;">int </span>c = <span style="color: #6897bb;">0</span><span style="color: #cc7832;">; </span>c < <span style="color: #9876aa;">replica</span><span style="color: #cc7832;">; </span>c++) {<br /> String key = String.<span style="font-style: italic;">format</span>(<span style="color: #6a8759;">"%s_%s"</span><span style="color: #cc7832;">, </span><span style="color: #9876aa;">nodeKey</span>.apply(node)<span style="color: #cc7832;">, </span>c)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: #9876aa;">ring</span>.put(<span style="color: #9876aa;">hashFunction</span>.apply(key.getBytes())<span style="color: #cc7832;">, </span>node)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> }<br /><br /> <span style="color: #cc7832;">public </span><span style="color: #507874;">T </span><span style="color: #ffc66d;">findSlot</span>(Object key) {<br /> <span style="color: #cc7832;">int </span>hash = <span style="color: #9876aa;">hashFunction</span>.apply(key.toString().getBytes())<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> return </span><span style="color: #9876aa;">ring</span>.getOrDefault(hash<span style="color: #cc7832;">, </span>findClosestSlot(hash))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #cc7832;">private </span><span style="color: #507874;">T </span><span style="color: #ffc66d;">findClosestSlot</span>(<span style="color: #cc7832;">int </span>hash) {<br /> SortedMap<Integer<span style="color: #cc7832;">, </span><span style="color: #507874;">T</span>> tail = <span style="color: #9876aa;">ring</span>.tailMap(hash)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> int </span>keyHash = tail.isEmpty() ? <span style="color: #9876aa;">ring</span>.firstKey() : tail.firstKey()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> return </span><span style="color: #9876aa;">ring</span>.get(keyHash)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br />}</pre></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Distributed Hash</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;"><span style="color: #cc7832;">public class </span>DistributedHashTable {<br /> <span style="color: #cc7832;">private final </span>ConsistentHashing<Node> <span style="color: #9876aa;">hash</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> public </span><span style="color: #ffc66d;">DistributedHashTable</span>(ConsistentHashing<Node> hash) {<br /> <span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">hash </span>= hash<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #cc7832;">public void </span><span style="color: #ffc66d;">put</span>(Object key<span style="color: #cc7832;">, </span>Object value) {<br /> findSlot(key).put(key<span style="color: #cc7832;">, </span>value)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #cc7832;">private </span>Node <span style="color: #ffc66d;">findSlot</span>(Object key) {<br /> <span style="color: #cc7832;">return </span><span style="color: #9876aa;">hash</span>.findSlot(key)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #cc7832;">public </span>Object <span style="color: #ffc66d;">get</span>(Object key) {<br /> <span style="color: #cc7832;">return </span>findSlot(key).get(key)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br />}</pre></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Consistent is very popular in many distributed systems like Casandra, DynamoDB,CouchBase, Akka Router, Riak and many more.</span></div><div><span style="font-family: verdana;"><br /></span></div><h3 style="text-align: left;"><span style="font-family: verdana;">Rendezvous Hashing</span></h3><div><div><span style="font-family: verdana;"><a href="https://www.eecs.umich.edu/techreports/cse/96/CSE-TR-316-96.pdf" target="_blank">Rendezvous hashing</a> is alternative to consistent hashing and its more general purpose. This also came at the same time but did not become that popular.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Idea is based on a score that is computed by using node & key, and the node with the highest score is picked up for handling requests. Hash function looks something like hash(node-x,key)</span></div></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvsF9AJCttm-h3Br6woj-gnCHC1EnpgC_5IsY35TOwHt3o0uxA-2FOLf6IjN1eZJRTPVsj1zGiF0I3y3OFVb1k036QJ5jJ8qq32Kib8xUntJ-vKV8al1my_0rXsI-oxJHuoMOXndNVoeJ0/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="935" data-original-width="2062" height="290" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvsF9AJCttm-h3Br6woj-gnCHC1EnpgC_5IsY35TOwHt3o0uxA-2FOLf6IjN1eZJRTPVsj1zGiF0I3y3OFVb1k036QJ5jJ8qq32Kib8xUntJ-vKV8al1my_0rXsI-oxJHuoMOXndNVoeJ0/w640-h290/image.png" width="640" /></a></div><br /><br /></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Few things that make this algorithm different</span></div><h4 style="text-align: left;"><span style="font-family: verdana;">Low overhead</span></h4><div><span style="font-family: verdana;">Picking the server is low overhead because it needs very less computation and no coordination is required.</span></div><div><span style="font-family: verdana;"><br /></span></div><h4 style="text-align: left;"><span style="font-family: verdana;">No coordination</span></h4><div><span style="font-family: verdana;">This property of the algorithm makes it very unique because all the nodes that are trying to identify a server for request processing will select the same server without communication with other nodes in cluster. Think like no global lock is required to make decision.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><h4 style="text-align: left;"><span style="font-family: verdana;">Heterogenous Servers</span></h4></div><div><div><span style="font-family: verdana;">One of the problems with consistent hashing is that some of the servers will be overloaded and to manage that additional replicas/virtual nodes are added and that result in a long list of servers to select from. This also becomes a problem when a cluster contains a server with different compute/storage capacity because now generic replica factor (i.e 3) can't be used.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">In this algorithm virtual servers are optional and only required if servers of different capacity are added in node.</span></div></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><h4 style="font-family: "Times New Roman"; text-align: left;"><span style="font-family: verdana;">Better load balancing</span></h4><div>Hash code has random property due to which each server has equal probability of getting picked.</div></span></div><div><h4 style="text-align: left;"><span style="font-family: verdana;">No duplicate work</span></h4></div><div><div><span style="font-family: verdana;">For eg client request is going to broadcast 10Gb data and doing this multiple times will definitely choke network bandwidth.</span></div><div><span style="font-family: verdana;">This algorithm gives guarantee that each request will only map to a single server with no/less coordination, so duplicate work can be totally avoided and it will result in saving extra network overhead.</span></div><div></div></div><div><span style="font-family: verdana;"><br /></span></div><h4 style="text-align: left;"><span style="font-family: verdana;">Minimal Disruption</span></h4><div><span style="font-family: verdana;">This property makes it very good selection especially when cluster is very dynamic. Incase of node failure only keys mapped to failed server needs to redistributed and it becomes very significant overhead when data set is huge.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Code snippet for node selection/assignment</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;"><span style="color: #cc7832;">public </span><span style="color: #507874;">N </span><span style="color: #ffc66d;">assignNode</span>(Object key) {<br /><br /> <span style="color: #cc7832;">long </span>value = Long.<span style="color: #9876aa; font-style: italic;">MIN_VALUE</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: #507874;">N </span>node = <span style="color: #cc7832;">null;<br /></span><span style="color: #cc7832;"> for </span>(<span style="color: #507874;">N </span>n : <span style="color: #9876aa;">nodes</span>.values()) {<br /> <span style="color: #cc7832;">long </span>newValue = <span style="color: #9876aa;">hash</span>.apply(n<span style="color: #cc7832;">, </span>key)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> if </span>(newValue > value) {<br /> node = n<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>value = newValue<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> }<br /> <span style="color: #cc7832;">return </span>node<span style="color: #cc7832;">;<br /></span>}</pre></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><a href="https://ignite.apache.org/" target="_blank">Apache Ignite</a> distributed database is based on Rendezvous hashing. Some other applications are cache, monitoring applications & exclusive task processing. </span></div><div><br /></div><div><br /></div><h2 style="text-align: left;">Range</h2><div><span style="font-family: verdana;">This is one of the simple and yet very powerful way for data partition and relational database has used it for ages.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Idea of range partition comes from how books are arrange in library or the ways words are put in dictionary. One of the important assumption to take advantage of this style is ordering of keys, if keys are random then this scheme is not good fit. </span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-9WtdkiQoATTz7HaeLXDkY6H2yPw-F5MkTqEVNlg0mZgdsO8-Dgg-gXdRgYeb04Rv28uxEpzbjL5cEirY29lUcFBxD33FaXv6RUR3WL2V-EygNt2CDKHS36hFcrpigNYQumMlkYbeY4Gj/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="268" data-original-width="724" height="236" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-9WtdkiQoATTz7HaeLXDkY6H2yPw-F5MkTqEVNlg0mZgdsO8-Dgg-gXdRgYeb04Rv28uxEpzbjL5cEirY29lUcFBxD33FaXv6RUR3WL2V-EygNt2CDKHS36hFcrpigNYQumMlkYbeY4Gj/w640-h236/image.png" width="640" /></a></div><br /><br /></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Range implementations can be vary based on need, lets look at some of options.</span></div><h3 style="text-align: left;"><span style="font-family: verdana;">Static Range</span></h3><div><span style="font-family: verdana;">As name suggest , it is static range and never changes over lifecycle of service. Static range works well if data is distributed evenly. Some of the good examples are time series data that can benefit by static ranges where time period ( i.e day/hour/minute) is one bucket. Some implementations adds little bit of dynamic nature to this style by creating new partition based on range pattern like every hour new partition is created.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Real data is skewed, so static range runs in problem when one of the partition becomes hot partition and whole system performance degrades and also not all the resources are utilized properly.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><h3><span style="font-family: verdana;">Dynamic Range</span></h3></div><div><div><span style="font-family: verdana;">Some of the new data systems add this innovation to make the system scalable. Let's look at how it works.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">It starts with an empty container with some capacity, in this example let's use 5.</span></div><div><span style="font-family: verdana;">Once the container is full then it splits in half, this process keeps on going. This way of dynamic partition creation handles skewed data and also a very scalable approach. </span></div></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5R6GbWv6ZmDt5SD1xrl8HclK6v1wyteoYYEElLMwQfb2QtF28N57qk3HRR3b57eM0QrC8fqIjDbyDKuGOLUXcK4osZuYQpY5r78sTXH284nCoJH5CiQnRHGUIDrO_oSJtWJJvabOI1rWU/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="953" data-original-width="1507" height="404" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5R6GbWv6ZmDt5SD1xrl8HclK6v1wyteoYYEElLMwQfb2QtF28N57qk3HRR3b57eM0QrC8fqIjDbyDKuGOLUXcK4osZuYQpY5r78sTXH284nCoJH5CiQnRHGUIDrO_oSJtWJJvabOI1rWU/w640-h404/image.png" width="640" /></a></div><br /><br /></span></div><div><br class="gmail-Apple-interchange-newline" /><span style="font-family: verdana;">In the above example adding 9 keys has created 3 nodes, each node containing an equal number of values. At the end partition will looking something like below</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZSLVooauIC2StWl108Sex00x7OuEL2TkPqC0sKAxPYfM8PzVQ3m1IUnHp0mtnazrDZZhMm28v9tkxkqFapG3X3k4Kq7_hjJ3Qzo5GqvhzptfqdNNiUEg_PMx6RJqLyMQZiJZ1vH2iFk_m/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="289" data-original-width="1102" height="168" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZSLVooauIC2StWl108Sex00x7OuEL2TkPqC0sKAxPYfM8PzVQ3m1IUnHp0mtnazrDZZhMm28v9tkxkqFapG3X3k4Kq7_hjJ3Qzo5GqvhzptfqdNNiUEg_PMx6RJqLyMQZiJZ1vH2iFk_m/w640-h168/image.png" width="640" /></a></div><br /><br /></span></div><div><span style="font-family: verdana;">Just to highlight again that ordering property is maintained during split process and makes it very good fit for efficient range scan.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Ordered data has many other benefits that can easily utilized query optimizer.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Any discussion on partition can't be completed without talking about request routing, answer to this question depends on the complexity that you want to push to clients.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiz3yGL5h_R2SYcNz71mFr17yp56z6xgjXd4CgTDIwjmx8Fj_bWT7AlzvD5HJa9IlglXfM1G7tHK_ZIv1d5XpRmqlfLsdg6EeA28GSC5moPAF_eXSHLSnSgxfV9KcdBceXN_vc7QPM1BI13/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="580" data-original-width="1158" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiz3yGL5h_R2SYcNz71mFr17yp56z6xgjXd4CgTDIwjmx8Fj_bWT7AlzvD5HJa9IlglXfM1G7tHK_ZIv1d5XpRmqlfLsdg6EeA28GSC5moPAF_eXSHLSnSgxfV9KcdBceXN_vc7QPM1BI13/w640-h320/image.png" width="640" /></a></div><br /><br /></span></div><div><span style="font-family: verdana;">In this design router has all the details of keys placement and it can route traffic.</span></div><div><br /></div><h1 style="text-align: left;">Conclusion</h1><div><div><div><span style="font-family: verdana;">Data partition is key for building a distributed system. It has many other challenges like how to rebalance partitions without any downtime, how to process query that needs access(i.e. read and write) to multiple partitions.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Partition replication comes after partition and it is also equally challenging.</span></div><div><span style="font-family: verdana;"><br /></span></div></div><div></div></div>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com2tag:blogger.com,1999:blog-6543397255913469784.post-74557871399500731972020-12-24T20:34:00.005-08:002020-12-24T20:34:47.279-08:00Ordered data structure using B+Tree and Skip List<p style="text-align: left;"><span style="font-family: verdana;">This post is based on <a href="https://draft.blogger.com/blog/post/edit/6543397255913469784/7455787139950073197#" target="_blank">rum-conjecture</a> paper for data systems. This paper talks about read, update & memory overhead of data structure and gives some nice examples on how balancing 2 factors leaves 3rd one in bad shape. </span></p><p></p><div> </div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhht2phArnHePHBm5O0aWqfZgiz9XtpSmUe8Du63U8IEqCsJZKwcxFxyL490gZ3xj5aX9phMoS_uFKn7WAp8-vXW8SdiQHsJJPVbHjdwMW3oJ5yCzIQEUREDBuayPWKa-mfEjGW6LYhkGXi/" style="margin-left: auto; margin-right: auto;"><img alt="" data-original-height="324" data-original-width="604" height="215" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhht2phArnHePHBm5O0aWqfZgiz9XtpSmUe8Du63U8IEqCsJZKwcxFxyL490gZ3xj5aX9phMoS_uFKn7WAp8-vXW8SdiQHsJJPVbHjdwMW3oJ5yCzIQEUREDBuayPWKa-mfEjGW6LYhkGXi/w400-h215/image.png" width="400" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Source : http://daslab.seas.harvard.edu/rum-conjecture/</td></tr></tbody></table><p><span style="font-family: verdana;"><br /><br /></span></p><p><span style="font-family: verdana;">Some of the popular read optimized ordered Key-value data structure are </span><a href="https://draft.blogger.com/blog/post/edit/6543397255913469784/7455787139950073197#" style="font-family: verdana;">BST</a><span style="font-family: verdana;"> , </span><a href="https://draft.blogger.com/blog/post/edit/6543397255913469784/7455787139950073197#" style="font-family: verdana;">Skip List</a><span style="font-family: verdana;"> , </span><a href="https://draft.blogger.com/blog/post/edit/6543397255913469784/7455787139950073197#" style="font-family: verdana;">Prefix Tree</a><span style="font-family: verdana;"> , </span><a href="https://draft.blogger.com/blog/post/edit/6543397255913469784/7455787139950073197#" style="font-family: verdana;">B+ tree</a><span style="font-family: verdana;"> etc.</span></p><p><span style="font-family: verdana;">To make read fast it does some trade off on space. </span></p><p><span style="font-family: verdana;">In this post I will share space(i.e memory) efficient data structure that takes ideas from B+Tree and Skip List to create a new ordered data structure. </span></p><p><span style="font-family: verdana;">Quick recap before we build our own data structure. </span></p><h2 style="text-align: left;"><span style="font-family: verdana;">B+ Tree</span></h2><p><span style="font-family: verdana;">B+Tree is variant BST, every node contains n children. Tree contains 2 types of nodes root, internal & leaves node(i.e external).</span></p><p><span style="font-family: verdana;">Leaves node contains all the entries. Root and internal nodes will contain just keys and pointers to the leaf node. </span></p><p><span style="font-family: verdana;">Every node has some capacity and when it gets full then <b><i>split process</i></b> is used to create another node that will have half of the values from the original node. This splitting helps in keeping allocation in control and also reduces rebalancing overhead as compared to BST.</span></p><p style="text-align: left;"><span style="font-family: verdana;"><br /></span></p><p style="text-align: left;"><span style="font-family: verdana;"></span></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9b1WxvK0b5pPQXlQ8NG-kaJ_aGCiNHRO9mJ-_0YQVDFNaM5tDJpYglaBBhaEWM89M6oPKcHvl-cJtZjxFclaa1R7VHXRI9xBKk6GNjTwl31FH6tWWQP67uvADZrcTHeEuv6olw_M5Qjl2/" style="margin-left: auto; margin-right: auto;"><img alt="" data-original-height="516" data-original-width="1121" height="294" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9b1WxvK0b5pPQXlQ8NG-kaJ_aGCiNHRO9mJ-_0YQVDFNaM5tDJpYglaBBhaEWM89M6oPKcHvl-cJtZjxFclaa1R7VHXRI9xBKk6GNjTwl31FH6tWWQP67uvADZrcTHeEuv6olw_M5Qjl2/w640-h294/image.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><b><i>B+Tree. Source:Wikipedia</i></b></td></tr></tbody></table><span style="font-family: verdana;"><br /><p style="font-family: "Times New Roman";"><span style="font-family: verdana;">This is a very popular data structure for databases, all relational databases and many non relational databases provide indexes that are backed up by B+Tree. </span></p><p style="font-family: "Times New Roman";"></p><p style="font-family: "Times New Roman";"><span style="font-family: verdana;">Its popularity has only increased over time.</span></p><p style="font-family: "Times New Roman";"><span style="font-family: verdana;">Now some databases are built using <a href="https://draft.blogger.com/blog/post/edit/6543397255913469784/7455787139950073197#">Log structured merge tree</a> that solves some of the issues with B+Tree.</span></p><p style="font-family: "Times New Roman";"><span style="font-family: verdana;">One of the trade off with B+Tree is that keys are stored at multiple places(leaf & internal nodes) and splits of nodes can cause cascade effects.</span></p><p style="font-family: "Times New Roman";"><span style="font-family: verdana;">Every value is wrapped up in some Node container and that causes extra 32 bit overhead per value and also extra indirection to get to value.</span></p><p style="font-family: "Times New Roman";"><span style="font-family: verdana;">Extra indirection does not work well with current CPU that have big cache lines. </span></p></span><h2 style="text-align: left;"><span style="font-family: verdana;">Skip List</span></h2><p style="text-align: left;"><span style="font-family: verdana;">Skip list takes ideas from the ordered link list and to make read and write efficient it adds </span><i style="font-family: verdana;"><b>upper layer or lane</b></i><span style="font-family: verdana;"> of sorted link list that has fewer values and acts like a fast lane to get to value.</span> </p><p style="text-align: left;"><span style="font-family: verdana;"> </span></p><p style="text-align: left;"><span style="font-family: verdana;"></span></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_5kb-mUI30FfBvXJSE3kZMx6YYzvDWIFtL10eaYxagURv5vQXoLBLnF0LYtH2FkTqPPGcPKtJUm1ZfiVd2P1rz1mbZi0y_CkgpR0ipLYpCzEP9Wavg7AQLlC4NJY15OTaa_AFY-WXV7OU/" style="margin-left: auto; margin-right: auto;"><img alt="" data-original-height="449" data-original-width="1920" height="150" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_5kb-mUI30FfBvXJSE3kZMx6YYzvDWIFtL10eaYxagURv5vQXoLBLnF0LYtH2FkTqPPGcPKtJUm1ZfiVd2P1rz1mbZi0y_CkgpR0ipLYpCzEP9Wavg7AQLlC4NJY15OTaa_AFY-WXV7OU/w640-h150/image.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;"><b><i>Skip List. Source:Wikipedia</i></b></td></tr></tbody></table><span style="font-family: verdana;"><br /><p style="font-family: "Times New Roman";"><span style="font-family: verdana;">This type of structure helps with concurrency because installing new value can be done with lock free operation like CAS.</span></p><p style="font-family: "Times New Roman";"></p><p style="font-family: "Times New Roman";"><span style="font-family: verdana;">One of the big trade-offs in skip lists is many keys are duplicated and level for key is decided randomly due to which every time skip levels could be very different although input is the same.</span></p><p style="font-family: "Times New Roman";"><span style="font-family: verdana;">This also has other memory overhead due to extra container objects required to hold the values like B+Tree.</span></p><p style="font-family: "Times New Roman";"><span style="font-family: verdana;">Let's look at new data structures that can take some ideas from these 2 data structures and reduce memory overhead and still maintain performance. </span></p></span><h2 style="text-align: left;"><span style="font-family: verdana;">B+Skip List</span></h2><p><span style="font-family: verdana;">I don't have any good name for this DS, so let's call it <i>B+SkipList</i>.</span></p><p><span style="font-family: verdana;">I will take few ideas </span></p><h3><span style="font-family: verdana;">- Ordered memory block</span></h3><p><span style="font-family: verdana;">B+Tree allocate nodes that can hold n values in an array. These values are stored in order by using simple array manipulation. This gives the nice property that related values are close together and have good chances to be in the same cache line. </span></p><h3><span style="font-family: verdana;">- Split node when full</span></h3><div><span style="font-family: verdana;">B+Tree split full node into 2 to balance level tree. </span></div><h3><span style="font-family: verdana;">- Skip Lanes</span></h3><p><span style="font-family: verdana;">Although SkipList is just an ordered link list but skip lane idea makes linked list like array, lower level nodes can be accessed quickly by skip lanes.</span></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;">Let's create something !</span></p><p><span style="font-family: verdana;">Ordered memory block is straightforward to implement. It will look something like below</span></p><p style="text-align: left;"><span style="font-family: verdana;"></span></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtcfua4mbSZBlRwCLzs-SJdaYZnq3mILjCoD8ESz3HarI_o3R9KK7sI461JHsD6DWddCaj36-jkbMNhYZANY49vcrVnJJ2s5Ms-BUfEkVZaKLbALY_Flk-E3BVsRWGRP2BDJSA67zPyHRC/" style="margin-left: auto; margin-right: auto;"><img alt="" data-original-height="634" data-original-width="391" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtcfua4mbSZBlRwCLzs-SJdaYZnq3mILjCoD8ESz3HarI_o3R9KK7sI461JHsD6DWddCaj36-jkbMNhYZANY49vcrVnJJ2s5Ms-BUfEkVZaKLbALY_Flk-E3BVsRWGRP2BDJSA67zPyHRC/w395-h640/image.png" width="395" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">Ordered Array</td></tr></tbody></table><p></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><br /></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: verdana;"><br /></span></div><span style="font-family: verdana;">Code snippet for ordered collection. I will take an example of int value for simplicity but the same thing applies for any custom object also.</span> <br /><p></p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;"><span style="color: #cc7832;">public </span><span style="color: #ffc66d;">OrderedIntCollection</span>(<span style="color: #cc7832;">int </span>maxElement) {<br /> <span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">maxElement </span>= maxElement<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> this</span>.<span style="color: #9876aa;">members </span>= <span style="color: #cc7832;">new int</span>[<span style="color: #6897bb;">1 </span>+ maxElement]<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> this</span>.<span style="color: #9876aa;">members</span>[<span style="color: #6897bb;">0</span>] = Integer.<span style="color: #9876aa; font-style: italic;">MAX_VALUE</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> this</span>.<span style="color: #9876aa;">length </span>= <span style="color: #6897bb;">0</span><span style="color: #cc7832;">;<br /></span>}<br /><br /><span style="color: #cc7832;">public void </span><span style="color: #ffc66d;">insert</span>(<span style="color: #cc7832;">int </span>value) {<br /> <span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">min </span>= Math.<span style="font-style: italic;">min</span>(<span style="color: #9876aa;">min</span><span style="color: #cc7832;">, </span>value)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> this</span>.<span style="color: #9876aa;">max </span>= Math.<span style="font-style: italic;">max</span>(<span style="color: #9876aa;">max</span><span style="color: #cc7832;">, </span>value)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> int </span>offSet = offSet(value<span style="color: #cc7832;">, </span><span style="color: #9876aa;">members</span><span style="color: #cc7832;">, </span><span style="color: #9876aa;">length</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> if </span>(<span style="color: #9876aa;">members</span>[offSet] == value) {<br /> <span style="color: #cc7832;">return; </span><span style="color: grey;">//This will skip copy overhead<br /></span><span style="color: grey;"> </span>}<br /><br /> shiftFrom(offSet)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: #9876aa;">members</span>[offSet] = value<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: #9876aa;">length</span>++<span style="color: #cc7832;">;<br /></span>}</pre><p style="text-align: left;"><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;">Our first building block is ready, let's move to split logic now.</span></p><h4><span style="font-family: verdana;">- Capacity check. </span></h4><p><span style="font-family: verdana;">This is required to decide if split is required or not.</span></p><h4><span style="font-family: verdana;">- Split logic.</span></h4><p><span style="font-family: verdana;">This will do an actual split and return a new node.</span></p><h4><span style="font-family: verdana;">- Additional metadata</span></h4><p><span style="font-family: verdana;">Now nodes can be split so we need to track some meta data like min, max value.</span></p><p><span style="font-family: verdana;">Split process might look like below</span></p><p style="text-align: left;"><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXqBQRCiL_WlLG1IWh_dOb_lfb4B9sxWXECv3m2ak6h5SzTqbjNUrWoov66Kz6JeF_RifJAWu0w5EhN_s8QTCRAPPbwMfbjuphH8lVpzanEGUxyDiF1KpmrVvCbxwRfYnZT_sawsnyPGjS/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="628" data-original-width="708" height="567" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXqBQRCiL_WlLG1IWh_dOb_lfb4B9sxWXECv3m2ak6h5SzTqbjNUrWoov66Kz6JeF_RifJAWu0w5EhN_s8QTCRAPPbwMfbjuphH8lVpzanEGUxyDiF1KpmrVvCbxwRfYnZT_sawsnyPGjS/w640-h567/image.png" width="640" /></a></span></div><span style="font-family: verdana;"><br /><br /></span><p></p><p><span style="font-family: verdana;">Split is one way to handle when a node is full and it has some advantage but other options are also available like extending current nodes and it could be a useful strategy in some cases to reduce memory fragmentation. We will talk about this later.</span></p><p><span style="font-family: verdana;"><br /></span></p><p></p><p></p><p></p><p style="color: black; font-family: "Times New Roman"; font-size: medium;"><span style="font-family: verdana;">Code snippet for split. It is simple array manipulation and makes sure that each node is independent and value can only contain 1 node. </span></p><p style="text-align: left;"><span style="font-family: verdana;"><br /></span></p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;"><span style="color: #cc7832;">public boolean </span><span style="color: #ffc66d;">hasCapacity</span>(<span style="color: #cc7832;">int </span>count) {<br /> <span style="color: #cc7832;">return </span><span style="color: #9876aa;">length </span>+ count < <span style="color: #9876aa;">members</span>.<span style="color: #9876aa;">length</span><span style="color: #cc7832;">;<br /></span>}<br /><span style="color: #cc7832;">public </span>OrderedIntCollection <span style="color: #ffc66d;">split</span>() {<br /> <span style="color: #cc7832;">int </span>half = <span style="color: #9876aa;">members</span>.<span style="color: #9876aa;">length </span>/ <span style="color: #6897bb;">2</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span><span style="color: grey;">//Split and allocate new<br /></span><span style="color: grey;"> </span><span style="color: #cc7832;">int</span>[] copy = <span style="color: #cc7832;">new int</span>[<span style="color: #6897bb;">1 </span>+ <span style="color: #9876aa;">maxElement</span>]<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> int </span>newLength = <span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">length </span>- half<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>System.<span style="font-style: italic;">arraycopy</span>(<span style="color: #9876aa;">members</span><span style="color: #cc7832;">, </span>half<span style="color: #cc7832;">, </span>copy<span style="color: #cc7832;">, </span><span style="color: #6897bb;">0</span><span style="color: #cc7832;">, </span>newLength)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>copy[newLength] = Integer.<span style="color: #9876aa; font-style: italic;">MAX_VALUE</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>OrderedIntCollection collection = <span style="color: #cc7832;">new </span>OrderedIntCollection(<span style="color: #9876aa;">maxElement</span><span style="color: #cc7832;">, </span>copy<span style="color: #cc7832;">, </span>newLength)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span><span style="color: grey;">//Update current<br /></span><span style="color: grey;"> </span><span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">members</span>[half] = Integer.<span style="color: #9876aa; font-style: italic;">MAX_VALUE</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> this</span>.<span style="color: #9876aa;">length </span>= half<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> this</span>.<span style="color: #9876aa;">max </span>= <span style="color: #9876aa;">members</span>[half - <span style="color: #6897bb;">1</span>]<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>Arrays.<span style="font-style: italic;">fill</span>(<span style="color: #9876aa;">members</span><span style="color: #cc7832;">, </span>half + <span style="color: #6897bb;">1</span><span style="color: #cc7832;">, </span><span style="color: #9876aa;">members</span>.<span style="color: #9876aa;">length</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">0</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> return </span>collection<span style="color: #cc7832;">;<br /></span></pre><p style="text-align: left;"><span style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;">}</span><span style="font-family: verdana;"> </span></p><p style="text-align: left;"><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;">Lets move to the final piece that is skip lanes.</span></p><p><span style="font-family: verdana;">With the split process now we can have multiple nodes and we have to keep track of all the nodes and maintain order of nodes.</span></p><p><span style="font-family: verdana;">Few things are required for lane data structure </span></p><p><span style="font-family: verdana;">- Dynamic list that allows you to add elements at any position.</span></p><p><span style="font-family: verdana;">- Ordering info for comparing nodes to identify which node comes first.</span></p><p><span style="font-family: verdana;">- Some way of finding which node will contain value.</span></p><p><span style="font-family: verdana;">- Some efficient algorithm to search values in all the available nodes.</span></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;">Let's enhance OrderedIntCollection to add ordering info</span></p><p style="text-align: left;"><span style="font-family: verdana;"></span></p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;"><span style="color: #cc7832;">public int </span><span style="color: #ffc66d;">max</span>() {<br /> <span style="color: #cc7832;">return </span><span style="color: #9876aa;">max</span><span style="color: #cc7832;">;<br /></span>}<br /><br /><span style="color: #cc7832;">public int </span><span style="color: #ffc66d;">min</span>() {<br /> <span style="color: #cc7832;">return </span><span style="color: #9876aa;">min</span><span style="color: #cc7832;">;<br /></span>}<br /><br /><span style="color: #bbb529;">@Override<br /></span><span style="color: #cc7832;">public int </span><span style="color: #ffc66d;">compareTo</span>(Integer value) {<br /> <span style="color: #cc7832;">if </span>(value < <span style="color: #9876aa;">min</span>) <span style="color: #cc7832;">return </span><span style="color: #6897bb;">1</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> else if </span>(value > <span style="color: #9876aa;">max</span>) <span style="color: #cc7832;">return </span>-<span style="color: #6897bb;">1</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> return </span><span style="color: #6897bb;">0</span><span style="color: #cc7832;">;<br /></span>}</pre><p style="text-align: left;"><span style="font-family: verdana;">Min/Max and compareTo function will help in having all the ordering info.</span></p><p style="text-align: left;"><span style="font-family: verdana;">compareTo function will be later used to find which node can contain value.</span></p><p style="text-align: left;"><span style="font-family: verdana;">With help of compareTo now we can store all the nodes in some array based data structure.</span></p><p style="text-align: left;"><span style="font-family: verdana;">At high level it might look at something like below</span></p><p style="text-align: left;"><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvME-2ZunWb4yEXqwpnGdk_zWLalzu7MQv2-_L0BYf7YNURtLipQfLiB_L1o1HFviLgItwKzc5VZjrr46oqrpE2k9l4ZB9NiZysoUaH884qoo4RgIxeLyU7HPiKpQFlVmORV-mkUxzQ1WU/" style="margin-left: auto; margin-right: auto;"><img alt="" data-original-height="1068" data-original-width="2048" height="334" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvME-2ZunWb4yEXqwpnGdk_zWLalzu7MQv2-_L0BYf7YNURtLipQfLiB_L1o1HFviLgItwKzc5VZjrr46oqrpE2k9l4ZB9NiZysoUaH884qoo4RgIxeLyU7HPiKpQFlVmORV-mkUxzQ1WU/w640-h334/image.png" width="640" /></a></td></tr><tr><td class="tr-caption" style="text-align: center;">B+ Skip List</td></tr></tbody></table><br /><br /><br /><br /></span></div><span style="font-family: verdana;"><p style="text-align: left;"></p><h3 style="text-align: left;">This layout has some nice property like</h3><h4 style="text-align: left;">- Keys are not duplicated.</h4><div style="text-align: left;"><div>This is a big gain and helps in reducing memory requirements. Only additional memory allocation is for lists that maintain reference to nodes. </div><div><h4></h4></div></div><div style="text-align: left;"><h4 style="text-align: left;">- Child node are just at 2nd level</h4><div>Child nodes can be always reached in just 2 hops, as compared to n ( tree depth) in b+tree or SkipList. </div><h4 style="text-align: left;">- Fanout of node is good.</h4><div>It gets fanout the same as B+Tree without overhead of additional internal nodes and it plays an important role in read/writes.</div><h4 style="text-align: left;">- Data locality in single node.</h4><div><div><div>Some of the discussion after the RUM paper was published was to add <b><i>Cache </i></b>also as dimension to look at data structure, so it becomes CRUM.</div><div><br /></div><div>Packing everything together helps with the <b><i>"C"</i></b> part as it provides better cache locality and this is one of the big gains because it can take some benefits of the cache line of the processors.</div><div><br /></div></div><p></p><p>Since nodes reference is stored in List based structure and are in order, so binary search can be used to identify nodes that are target for read or write.</p><p>Read/Write can be done in Log N time. Once a node is identified then it is sequential scan to locate value and that can add some cost but it is possible to use binary search within the node also to reduce overhead.</p><p></p></div></div><h3 style="text-align: left;">Trade off</h3></span></div></div><span style="font-family: verdana;">Some of the trade off</span><h3 style="text-align: left;"><span style="font-family: verdana; font-size: small;">Binary search can't be used 100% for writing.</span></h3><p style="text-align: left;"><span style="font-family: verdana;">Read request can be always served by binary search but for writing we need a hybrid approach, for eg in above sample if value is inserted that does not fall in any range then sequential search is required to find right node. Some of the sample value that can trigger sequential search are 1 (i.e min value), 300(i.e max value),105 (i.e comes in between node 2 and 3) </span></p><div><h3 style="text-align: left;"><span style="font-family: verdana; font-size: medium;">Fragmentation</span></h3><div><div><span style="font-family: verdana;">Due to splitting rules we might see many nodes that are half filled but that issue can be solved by relaxing the splitting rule like allow to choose between split or extend based on data distribution. </span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Compact nodes by merging content. </span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Code snippet of Skip Lane.</span></div></div><div><span style="font-family: verdana;"><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;"><span style="color: #cc7832;">public class </span>OrderedInts {<br /> <span style="color: #cc7832;">private final </span>List<OrderedIntCollection> <span style="color: #9876aa;">values </span>= <span style="color: #cc7832;">new </span>ArrayList<>()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> private final int </span><span style="color: #9876aa;">blockSize</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> private final </span>AtomicLong <span style="color: #9876aa;">bsHit </span>= <span style="color: #cc7832;">new </span>AtomicLong()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> private final </span>AtomicLong <span style="color: #9876aa;">seqHit </span>= <span style="color: #cc7832;">new </span>AtomicLong()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> public </span><span style="color: #ffc66d;">OrderedInts</span>(<span style="color: #cc7832;">int </span>blockSize) {<br /> <span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">blockSize </span>= blockSize<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #cc7832;">public void </span><span style="color: #ffc66d;">insert</span>(<span style="color: #cc7832;">int </span>value) {<br /> OrderedIntCollection e = search(value)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>e.insert(value)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #cc7832;">private </span>OrderedIntCollection <span style="color: #ffc66d;">search</span>(<span style="color: #cc7832;">int </span>value) {<br /> <span style="color: #cc7832;">if </span>(<span style="color: #9876aa;">values</span>.isEmpty()) {<br /> OrderedIntCollection last = <span style="color: #cc7832;">new </span>OrderedIntCollection(<span style="color: #9876aa;">blockSize</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: #9876aa;">values</span>.add(last)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> return </span>last<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>} <span style="color: #cc7832;">else </span>{<br /> <span style="color: #cc7832;">int </span>position = <span style="font-style: italic;">binarySearch</span>(<span style="color: #9876aa;">values</span><span style="color: #cc7832;">, </span>value)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> if </span>(position >= <span style="color: #6897bb;">0</span>) {<br /> <span style="color: #9876aa;">bsHit</span>.incrementAndGet()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> return </span>splitIfRequired(value<span style="color: #cc7832;">, </span>position<span style="color: #cc7832;">, </span><span style="color: #9876aa;">values</span>.get(position))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>} <span style="color: #cc7832;">else </span>{<br /> <span style="color: #9876aa;">seqHit</span>.incrementAndGet()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> return </span>sequentialSearch(value)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> }<br /> }</pre><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;">}</pre></span></div><div><span style="font-family: verdana;"><br /></span></div><div><h2 style="text-align: left;"><span style="font-family: verdana;">Conclusion</span></h2></div></div><div><span style="font-family: verdana;">In computer science we only have trade off and nothing comes for free.</span></div><div><span style="font-family: verdana;">RUM way of looking at data structure provides key insights on trade-off and also provides some ideas around new data structure possibility.</span></div><div><span style="font-family: verdana;"> </span></div>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com22tag:blogger.com,1999:blog-6543397255913469784.post-13578348338587726392020-12-16T07:36:00.001-08:002020-12-16T07:36:32.765-08:00Sparse Matrix<p> <span style="font-family: verdana;">Vectors are a very powerful data structure and it allows to write/read data using index. These vectors can be combined to create a matrix. Matrix are used in numerical analysis. </span><span style="font-family: verdana;"> </span></p><p><span style="font-family: verdana;">One of the problems with matrix is that most of the real world matrix are sparse and the traditional way of allocating 2 Dimensional arrays to represent matrix can waste lots of space and also need lots of upfront memory.</span></p><p><span style="font-family: verdana;">In this post I will share some ways of storing sparse matrix.</span></p><p><span style="font-family: verdana;">In this example I will use Integer value as an example but these techniques can be used for any data type.</span></p><h2 style="text-align: left;"><span style="font-family: verdana;">Dense Matrix</span></h2><p style="text-align: left;"><span style="font-family: verdana;">This is the most straightforward and default way. This is also most efficient for read and write access but needs a full matrix to be allocated before using it. </span> </p><p style="text-align: left;"><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisGQvHaRicLRRNzoCO-ozXX-0E9Cxh0LaHgNXgM1GiwF1u7bxPaHLbiPX_DUF3IqiPCCfk3v4qzxqM1eqCychiq7jNLIdcQpBbBm74bqa0N9y_sbAQQBUBtmdUjBL3wAPK0Ni_RikPM7e_/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="306" data-original-width="346" height="354" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisGQvHaRicLRRNzoCO-ozXX-0E9Cxh0LaHgNXgM1GiwF1u7bxPaHLbiPX_DUF3IqiPCCfk3v4qzxqM1eqCychiq7jNLIdcQpBbBm74bqa0N9y_sbAQQBUBtmdUjBL3wAPK0Ni_RikPM7e_/w400-h354/image.png" width="400" /></a></span></div><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><br /></span></div><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><br /></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: verdana;"><br /></span></div><p></p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;"><span style="color: #cc7832;">public class </span>IntDenseVectorValue {<br /><br /> <span style="color: #cc7832;">final int</span>[][] <span style="color: #9876aa;">values</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> public </span><span style="color: #ffc66d;">IntDenseVectorValue</span>(<span style="color: #cc7832;">int </span>xSize<span style="color: #cc7832;">, int </span>ySize) {<br /> <span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">values </span>= <span style="color: #cc7832;">new int</span>[xSize][ySize]<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #cc7832;">public void </span><span style="color: #ffc66d;">set</span>(<span style="color: #cc7832;">int </span>x<span style="color: #cc7832;">, int </span>y<span style="color: #cc7832;">, int </span>value) {<br /> <span style="color: #9876aa;">values</span>[x][y] = value<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /><br /> <span style="color: #cc7832;">public int </span><span style="color: #ffc66d;">get</span>(<span style="color: #cc7832;">int </span>x<span style="color: #cc7832;">, int </span>y) {<br /> <span style="color: #cc7832;">return </span><span style="color: #9876aa;">values</span>[x][y]<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br />}</pre><p style="text-align: left;"><span style="font-family: verdana;">Many real world data sets are sparse, so lots of space gets wasted in such situations.</span> </p><h2 style="text-align: left;">Block Dense Matrix</h2><p><span style="font-family: verdana;">This technique is based on creating small blocks of W width and each block is a small 2 dimension array.</span></p><p><span style="font-family: verdana;">During set/get X variables are used to identify block numbers.</span></p><p><span style="font-family: verdana;"><br /></span></p><p></p><div class="separator" style="clear: both; font-family: verdana; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9OSyx9DMy-t8nr0QVyz-to8w-xnrhfxJ2QG7TBQBGGmPbAbntPfea49JTNcwAijMdpjD6GqsI3fApsJEvQUkvS9kEy6UdeFWwFgamVWoEbpauFx-rO1XokFEWmRJOSKSwE3OtpjIa5gZm/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="296" data-original-width="382" height="310" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9OSyx9DMy-t8nr0QVyz-to8w-xnrhfxJ2QG7TBQBGGmPbAbntPfea49JTNcwAijMdpjD6GqsI3fApsJEvQUkvS9kEy6UdeFWwFgamVWoEbpauFx-rO1XokFEWmRJOSKSwE3OtpjIa5gZm/w400-h310/image.png" width="400" /></a></div><br /><span style="font-family: verdana;">This approach avoids upfront allocation and a very good fix for incremental data load. Indirection layer is added with help of Map actual block is located. Performance wise this is the same as a Dense vector but with less memory overhead.</span> <br /><p></p><p><span style="font-family: verdana;">Code snippet for block dense matrix.</span></p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;"><span style="color: #cc7832;">public class </span>IntDenseBlockVectorValue {<br /> <span style="color: #cc7832;">final int </span><span style="color: #9876aa;">blockSize</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> final int </span><span style="color: #9876aa;">width</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> final int </span><span style="color: #9876aa;">length</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> final </span>Map<Integer<span style="color: #cc7832;">, int</span>[][]> <span style="color: #9876aa;">values </span>= <span style="color: #cc7832;">new </span>HashMap<>()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> public </span><span style="color: #ffc66d;">IntDenseBlockVectorValue</span>(<span style="color: #cc7832;">int </span>x<span style="color: #cc7832;">, int </span>y<span style="color: #cc7832;">, int </span>blockSize) {<br /> <span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">length </span>= x<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> this</span>.<span style="color: #9876aa;">width </span>= y<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> this</span>.<span style="color: #9876aa;">blockSize </span>= blockSize<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #cc7832;">public void </span><span style="color: #ffc66d;">set</span>(<span style="color: #cc7832;">int </span>x<span style="color: #cc7832;">, int </span>y<span style="color: #cc7832;">, int </span>value) {<br /> check(x<span style="color: #cc7832;">, </span>y)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> int </span>blockIndex = x / <span style="color: #9876aa;">blockSize</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> int</span>[][] block = <span style="color: #9876aa;">values</span>.computeIfAbsent(blockIndex<span style="color: #cc7832;">, </span>index -> <span style="color: #cc7832;">new int</span>[<span style="color: #9876aa;">blockSize</span>][<span style="color: #9876aa;">width</span>])<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> int </span>rowIndex = x % <span style="color: #9876aa;">blockSize</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>block[rowIndex][y] = value<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #cc7832;">private void </span><span style="color: #ffc66d;">check</span>(<span style="color: #cc7832;">int </span>x<span style="color: #cc7832;">, int </span>y) {<br /> <span style="color: #cc7832;">if </span>(x >= <span style="color: #9876aa;">length </span>|| y >= <span style="color: #9876aa;">width</span>) {<br /> <span style="color: #cc7832;">throw new </span>ArrayIndexOutOfBoundsException(String.<span style="font-style: italic;">format</span>(<span style="color: #6a8759;">"Index [%s,%s] does not exists"</span><span style="color: #cc7832;">, </span>x<span style="color: #cc7832;">, </span>y))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> }<br /><br /> <span style="color: #cc7832;">public int </span><span style="color: #ffc66d;">get</span>(<span style="color: #cc7832;">int </span>x<span style="color: #cc7832;">, int </span>y) {<br /> check(x<span style="color: #cc7832;">, </span>y)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> int </span>blockIndex = x / <span style="color: #9876aa;">blockSize</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> int</span>[][] block = <span style="color: #9876aa;">values</span>.get(blockIndex)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> int </span>rowIndex = x % <span style="color: #9876aa;">blockSize</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> return </span>block[rowIndex][y]<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /></pre><p><span style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;">}</span><span style="font-family: verdana;"> </span></p><h2 style="text-align: left;"><span style="font-family: verdana;">Sparse Row Matrix</span></h2><p><span style="font-family: verdana;">This technique takes block allocation to the next level by doing allocation at row level.</span></p><p><span style="font-family: verdana;">Map contains an entry for every row and a single dimension vector is used as a value. </span></p><p style="text-align: left;"><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPEVywzyuByuEBf6J-SEpfsaQaCjtDQ0fZ8bFRH8htjRWwByOp5bwNZmZQbC03OqCfMg1zjFzr5wEqdkxHTA6bnL6n7p5laZ25l8xK67_amFQU8UV6BiPajFtcCq0v2wt8n9mjgimjNTF3/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="280" data-original-width="400" height="280" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPEVywzyuByuEBf6J-SEpfsaQaCjtDQ0fZ8bFRH8htjRWwByOp5bwNZmZQbC03OqCfMg1zjFzr5wEqdkxHTA6bnL6n7p5laZ25l8xK67_amFQU8UV6BiPajFtcCq0v2wt8n9mjgimjNTF3/w400-h280/image.png" width="400" /></a></span></div><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><br /></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: verdana;">This helps in reducing wastage to a great extent. If some rows are not added then that row never gets allocated.</span> </div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: verdana;"><br /></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: verdana;">Code snippet</span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: verdana;"><br /></span></div><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;"><br /><span style="color: #cc7832;">public class </span>IntSparseRowVectorValue {<br /> <span style="color: #cc7832;">private final </span>Map<Integer<span style="color: #cc7832;">, int</span>[]> <span style="color: #9876aa;">values </span>= <span style="color: #cc7832;">new </span>HashMap<>()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> private final int </span><span style="color: #9876aa;">width</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> public </span><span style="color: #ffc66d;">IntSparseRowVectorValue</span>(<span style="color: #cc7832;">int </span>width) {<br /> <span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">width </span>= width<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #cc7832;">public void </span><span style="color: #ffc66d;">set</span>(<span style="color: #cc7832;">int </span>x<span style="color: #cc7832;">, int </span>y<span style="color: #cc7832;">, int </span>value) {<br /> <span style="color: #cc7832;">int</span>[] row = <span style="color: #9876aa;">values</span>.computeIfAbsent(x<span style="color: #cc7832;">, </span>index -> <span style="color: #cc7832;">new int</span>[<span style="color: #9876aa;">width</span>])<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>row[y] = value<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #cc7832;">public int </span><span style="color: #ffc66d;">get</span>(<span style="color: #cc7832;">int </span>x<span style="color: #cc7832;">, int </span>y) {<br /> <span style="color: #cc7832;">int</span>[] row = <span style="color: #9876aa;">values</span>.get(x)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> return </span>row[y]<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /></pre><div class="separator" style="clear: both; text-align: left;"><span style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;">}</span><span style="font-family: verdana;"> </span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: verdana;"><br /></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: verdana;"><br /></span></div><h2 style="clear: both; text-align: left;"><span style="font-family: verdana;">Sparse Row/Column Matrix</span></h2><p><span style="font-family: verdana;">This decomposed row vector to individual columns and gives ultimate memory gain with some trade off of read/write access.</span></p><p><span style="font-family: verdana;">This is modeled as Map of Map.</span></p><p style="text-align: left;"><span style="font-family: verdana;"><br /></span></p><p style="text-align: left;"><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjumuc3jxIhbUM3OfDP-oRaUiwFEtiRcE6WP7r4hv_fBYmaEQPpv7nH_JWM2fs0KVad3DULjFdgI6WbnpsHi1mRsgvqoRalE18i3LcdWMnQ-M7T1d8ufT4YWAVCRSNm20fettQ0Pmq3ZYwJ/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="404" data-original-width="372" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjumuc3jxIhbUM3OfDP-oRaUiwFEtiRcE6WP7r4hv_fBYmaEQPpv7nH_JWM2fs0KVad3DULjFdgI6WbnpsHi1mRsgvqoRalE18i3LcdWMnQ-M7T1d8ufT4YWAVCRSNm20fettQ0Pmq3ZYwJ/w368-h400/image.png" width="368" /></a></span></div><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><br /></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: verdana;">Code snippet</span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: verdana;"><br /></span></div><p></p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;"><span style="color: #cc7832;">public class </span>SparseRowColVectorValue {<br /><br /> <span style="color: #cc7832;">private final </span>Map<Integer<span style="color: #cc7832;">, </span>Map<Integer<span style="color: #cc7832;">, </span>Integer>> <span style="color: #9876aa;">values </span>= <span style="color: #cc7832;">new </span>HashMap<>()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> public void </span><span style="color: #ffc66d;">set</span>(<span style="color: #cc7832;">int </span>x<span style="color: #cc7832;">, int </span>y<span style="color: #cc7832;">, int </span>value) {<br /> Map<Integer<span style="color: #cc7832;">, </span>Integer> row = <span style="color: #9876aa;">values</span>.computeIfAbsent(x<span style="color: #cc7832;">, </span>index -> <span style="color: #cc7832;">new </span>HashMap<>())<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>row.put(y<span style="color: #cc7832;">, </span>value)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #cc7832;">public int </span><span style="color: #ffc66d;">get</span>(<span style="color: #cc7832;">int </span>x<span style="color: #cc7832;">, int </span>y) {<br /> Map<Integer<span style="color: #cc7832;">, </span>Integer> row = <span style="color: #9876aa;">values</span>.get(x)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> return </span>row.get(y)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /></pre><p style="text-align: left;"><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: left;"><span style="font-family: verdana;"><span style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;">}</span> </span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: verdana;"><br /></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: verdana;">One thing to note in this approach is that the default map of JDK will need a wrapper of primitive type to store value and it can result in some performance overhead due to boxing/unboxing. This issue can be solved by having a primitive Map. </span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: verdana;"><br /></span></div><div class="separator" style="clear: both; text-align: left;"><span style="font-family: verdana;"> </span></div><h2 style="text-align: left;"><span style="font-family: verdana;">Conclusion</span></h2><p style="text-align: left;"><span style="font-family: verdana;">Each of sparse vector representation is special purpose and should be used based on usecase. All of these types can be mixed by having abstraction on top of it that will dynamically identify correct matrix implementation based on underlying data.</span></p><p style="text-align: left;"><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIGs2zPElPYwE4OMQeHhk4QXqjG6YYPuiQFkVcs1aZES9p0HuDf6PdpMxu555xrVYqmKbhTMoTEYI88u0pswxrx0DcQhF6NnKqbUrCv-MYIsmbHWZj7e-shFvXYrqL2MvmvxtVanIi1akS/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="478" data-original-width="558" height="343" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIGs2zPElPYwE4OMQeHhk4QXqjG6YYPuiQFkVcs1aZES9p0HuDf6PdpMxu555xrVYqmKbhTMoTEYI88u0pswxrx0DcQhF6NnKqbUrCv-MYIsmbHWZj7e-shFvXYrqL2MvmvxtVanIi1akS/w400-h343/image.png" width="400" /></a></span></div><span style="font-family: verdana;"><br /><br /></span><p></p><p style="text-align: left;"><span style="font-family: verdana;"> </span></p><p style="text-align: left;"><span style="font-family: verdana;"><br /></span></p><p style="text-align: left;"><span style="font-family: verdana;"><br /></span></p><span style="font-family: verdana;"><br /></span><p></p><div class="separator" style="clear: both; text-align: left;"><span style="font-family: verdana;"><br /></span></div><span style="font-family: verdana;"><br /><br /></span><p></p>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com0tag:blogger.com,1999:blog-6543397255913469784.post-60262907487056757982020-12-13T00:30:00.000-08:002020-12-13T00:30:27.328-08:00Sorting under constraint algorithms<p><span style="font-family: verdana;">In the compiler world code inlining is the most important optimization that enables other optimization like dead code elimination, pre fetching , out of order execution etc.</span></p><p><span style="font-family: verdana;">Similarly sorting also enables many optimizations like binary search , streaming aggregation, range search operations, prefix compression, run length encoding , delta encoding, understanding trends, posting list, data partition etc.</span></p><p><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQr_TwJfuKDDxSVR2Z1zvthDv9wfwpWPLRQjanVrSMQoA7porQGH6pZ_13VPe5UsA6VT35I-XsRlCI012KGkSJHZZA7hWP02hJsSHvMrMi0bOba_bwzXkPK5qJHjKdiDpIpOExhtJNFDlV/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="168" data-original-width="300" height="179" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQr_TwJfuKDDxSVR2Z1zvthDv9wfwpWPLRQjanVrSMQoA7porQGH6pZ_13VPe5UsA6VT35I-XsRlCI012KGkSJHZZA7hWP02hJsSHvMrMi0bOba_bwzXkPK5qJHjKdiDpIpOExhtJNFDlV/" width="320" /></a></span></div><span style="font-family: verdana;"><br /><br /></span><p></p><p><span style="font-family: verdana;">Sorting is memory and compute intensive work and many times we don't have enough compute/memory to do it.</span></p><p><span style="font-family: verdana;">In this post I will share 2 sorting algorithms that can be used when a system has memory or CPU constraint.</span></p><h2 style="text-align: left;"><span style="font-family: verdana;">Disk based sorting</span></h2><p><span style="font-family: verdana;">We have file containing 1 TB data and we want to sort it. Data is huge to it is not possible to use standard in-memory sorting algorithm for this. </span></p><p><span style="font-family: verdana;">One way to handle sorting of such data is split it in chunks, sort the chunk in memory, persist chunk to disk and finally merge the sorted chunk using k way merge algorithm.</span></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;">At high level sort pipeline will look something like below </span></p><p style="text-align: left;"><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXBPWohePcM8rBJ9Z0Mw22p9BRb4rvf7bu37Aap8XG4gE8apeiDBzV2XkqV69BG7UJAOpSrG0ow9sDRIy-Sctd0bT6YFxE0q7sBM8NfMjgQciLIIfA0eP0lTovWS-Qx87sxgNFN2hG_eYQ/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="378" data-original-width="815" height="296" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXBPWohePcM8rBJ9Z0Mw22p9BRb4rvf7bu37Aap8XG4gE8apeiDBzV2XkqV69BG7UJAOpSrG0ow9sDRIy-Sctd0bT6YFxE0q7sBM8NfMjgQciLIIfA0eP0lTovWS-Qx87sxgNFN2hG_eYQ/w640-h296/image.png" width="640" /></a></span></div><span style="font-family: verdana;"><br /><br /></span></div><p><span style="font-family: verdana;">Nice thing about this algorithm is that it is <a href="https://draft.blogger.com/blog/post/edit/6543397255913469784/6026290748705675798#" target="_blank">Embarrassingly_parallel</a>.</span></p><p><span style="font-family: verdana;">This algorithm</span><span style="font-family: verdana;"> is also good example of </span><a href="https://en.wikipedia.org/wiki/Divide-and-conquer_algorithm" style="font-family: verdana;" target="_blank">Divide-and-conquer_algorithm</a><span style="font-family: verdana;"> and this technique can be applied</span><span style="font-family: verdana;"> both the stages.</span></p><p><span style="font-family: verdana;">This algorithm has 2 stages in the pipeline that can be executed in parallel to take advance of multiple cores.</span></p><p><span style="font-family: verdana;">Lets assume that input file contains 10 Million then it can be decomposed in <b><i>Split </i></b>stage</span></p><p><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMaqKsE8Sv7sPhVo2NJJak_IHg51iZVTUFDkplkOAFw0cUCf2LWbvglqwxv0u6kmKQ-qw0CoK_AUJsugghp88YsW30wUJIU8qs7RZ7LNLG7uKsVfo3i_HaAPlDYSa9op97ItVtDUA7Ub4w/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="447" data-original-width="767" height="373" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMaqKsE8Sv7sPhVo2NJJak_IHg51iZVTUFDkplkOAFw0cUCf2LWbvglqwxv0u6kmKQ-qw0CoK_AUJsugghp88YsW30wUJIU8qs7RZ7LNLG7uKsVfo3i_HaAPlDYSa9op97ItVtDUA7Ub4w/w640-h373/image.png" width="640" /></a></div><br />In merge stage we have to do reverse operations of taking multiple files and creating single one.<p></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLlm2NLLfpYrrZ7FnR3YBb2Fd6kpPzVeK2-GW0GwaQciwCG6CwUIJC0WDnuW9n5ZSK-jCD-4Dd_bjnShUbems_SFTb17mlcatewtdqMEub25ftVZI5Yxi4ScZ8ZSebUZEuMBtvoDMIAxGO/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="546" data-original-width="755" height="462" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLlm2NLLfpYrrZ7FnR3YBb2Fd6kpPzVeK2-GW0GwaQciwCG6CwUIJC0WDnuW9n5ZSK-jCD-4Dd_bjnShUbems_SFTb17mlcatewtdqMEub25ftVZI5Yxi4ScZ8ZSebUZEuMBtvoDMIAxGO/w640-h462/image.png" width="640" /></a></div><br /><br /><p></p><p style="text-align: left;"><span style="font-family: verdana;">Split & Merge has different types of compute/disk requirement and it is possible to make both the stage parallel or just one based on constraint.</span></p><p style="text-align: left;"><span style="font-family: verdana;">Overall sort pipeline will look like below. </span><span style="font-family: verdana;"> </span></p><p style="text-align: left;"><span style="font-family: verdana;"><br /></span></p><p style="text-align: left;"><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTSUi0gQ9EVrZI8vCJYroDJ3CPO521qF23Vo3XdbhVkL4CoImjpSyX1vMbMeTq39zGfKkzE4BV369kKktdKQbiu2ARMoJ4_ttrGEw82j-CXa2Gn86FL6lAYqaujRLaL_IeeQ2yWX0SjiAI/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="737" data-original-width="757" height="622" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTSUi0gQ9EVrZI8vCJYroDJ3CPO521qF23Vo3XdbhVkL4CoImjpSyX1vMbMeTq39zGfKkzE4BV369kKktdKQbiu2ARMoJ4_ttrGEw82j-CXa2Gn86FL6lAYqaujRLaL_IeeQ2yWX0SjiAI/w640-h622/image.png" width="640" /></a></span></div><span style="font-family: verdana;"><br /></span><p><span style="font-family: verdana;">This algorithm is used by many databases to manage result sets that can't be fit into memory. <br /><br /></span></p><p></p><p><span style="font-family: verdana;">Important logic in this algorithm is K-Way merge of sorted results. If K is 2 then it is straight forward.</span></p><p></p><h3 style="text-align: left;"><span style="font-family: verdana;">2 Way merge</span></h3><div><div><span style="font-family: verdana;">Merge process is pick head from both iterators and add the value that is less, move pointer of iterator whose value was added.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Need to handle some edge conditions to avoid buffer overflow while reading and handling iterators of different size.</span></div></div><div><span style="font-family: verdana;"><br /></span></div><div><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;"><span style="color: #cc7832;"><br /></span>v1.next()<span style="color: #cc7832;">;<br /></span>v2.next()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;">while </span>(v1.hasNext() && v2.hasNext()) {<br /> value1 = v1.value()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>value2 = v2.value()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> if </span>(isLessThan(value1<span style="color: #cc7832;">, </span>value2)) {<br /> buffer.add(value1)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>v1.next()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>} <span style="color: #cc7832;">else </span>{<br /> buffer.add(value2)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>v2.next()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br />}<br /><br /><span style="color: #cc7832;">if </span>(v1.hasNext()) {<br /> append(buffer<span style="color: #cc7832;">, </span>v1)<span style="color: #cc7832;">;<br /></span>} <span style="color: #cc7832;">else if </span>(v2.hasNext()) {<br /> append(buffer<span style="color: #cc7832;">, </span>v2)<span style="color: #cc7832;">;<br /></span>}</pre></div><p style="text-align: left;"><span style="font-family: verdana;"></span></p><h3><span style="font-family: verdana;">K Way merge</span></h3><div><div><span style="font-family: verdana;">Assume K is 4, then one way to merge is to split the whole list in pairs of 2, keep merging in pairs and finally start merge out of 2 way merges. This is a good algorithm but can't take advantage of batching multiple iterators.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Recommended way is to use Heap of K values. This is more efficient as we can process multiple inputs in a single pass and can reduce IO overhead also. </span></div></div><div><br /></div><div><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;"><pre style="font-family: "JetBrains Mono", monospace; font-size: 9pt;">PriorityQueue<LineIterator> heap=...<br />LineIterator itr<span style="color: #cc7832;">;</span></pre><pre style="font-family: "JetBrains Mono", monospace; font-size: 9pt;"><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;">while </span>((itr = heap.poll()) != <span style="color: #cc7832;">null</span>) {<br /> <span style="font-style: italic;">write</span>(writer<span style="color: #cc7832;">, </span>itr.value())<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>itr.next()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> if </span>(itr.hasNext()) {<br /> heap.add(itr)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br />}</pre></pre></div><div><br /></div><h2 style="text-align: left;">BitMap Sorting</h2><p><span style="font-family: verdana;">Bitmap is a powerful data structure for searching and has some interesting properties for sort also.</span></p><p><span style="font-family: verdana;">Consider a scenario where the file contains n positive integer and each value is less than K.</span></p><p><span style="font-family: verdana;">K can be really huge depending on max value, to put this in context just by using 256 MB memory billions of int values can be sorted.</span></p><p><span style="font-family: verdana;">Idea is based around an allocated array with every element of K word (i.e 32 or 64). If we used 32 bit words then 32 values can be stored in every slot. Total capacity of this data structure is 32 * len(array).</span></p><p><span style="font-family: verdana;">Setting bit needs 2 information, slot in array and position in that slot.</span></p><p style="text-align: left;"><span style="font-family: verdana;"><br /></span></p><p style="text-align: left;"><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgC0_YaH25BmtOxC6FZkBPo-Mk37WHBHx_wvunmfc8o7YcFQjB9c1byn54A8ZgvSAd7m9Upt17s6P_kuIQ2wvou7VrGiEs6EQb4xlXREqSyZnqRUEHLfGEGHBe82_wENrOYbClTZNA9Z-FJ/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="520" data-original-width="495" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgC0_YaH25BmtOxC6FZkBPo-Mk37WHBHx_wvunmfc8o7YcFQjB9c1byn54A8ZgvSAd7m9Upt17s6P_kuIQ2wvou7VrGiEs6EQb4xlXREqSyZnqRUEHLfGEGHBe82_wENrOYbClTZNA9Z-FJ/w608-h640/image.png" width="608" /></a></span></div><span style="font-family: verdana;"><br /><br /></span><p></p><p><span style="font-family: verdana;">Bit fiddling enables to pack multiple values in a single word, you want to read more on bit fiddling then refer to <a href="https://draft.blogger.com/blog/post/edit/6543397255913469784/6026290748705675798#" target="_blank">bit-fiddling</a>.</span></p><p><span style="font-family: verdana;">In this example bytes is 4 and word size is 32.</span></p><p><span style="font-family: verdana;">Checking for value is straightforward and it involves doing bit wise & on Slot value.</span></p><p style="text-align: left;"><span style="font-family: verdana;">Complete working code </span></p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;"><span style="color: #cc7832;">public static final int </span><span style="color: #9876aa; font-style: italic;">NO_OF_BYTE </span>= <span style="color: #6897bb;">4</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;">private final int </span><span style="color: #9876aa;">WORD_SIZE </span>= <span style="color: #6897bb;">8 </span>* <span style="color: #9876aa; font-style: italic;">NO_OF_BYTE</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;">private final int </span><span style="color: #9876aa;">SLOT_SHIFT </span>= <span style="color: #9876aa; font-style: italic;">NO_OF_BYTE </span>+ <span style="color: #6897bb;">1</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;">private final int</span>[] <span style="color: #9876aa;">values</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;">private final int </span><span style="color: #9876aa;">maxValue</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;">public </span><span style="color: #ffc66d;">BitMapSort</span>(<span style="color: #cc7832;">int </span>maxValue) {<br /> <span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">maxValue </span>= maxValue<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> this</span>.<span style="color: #9876aa;">values </span>= <span style="color: #cc7832;">new int</span>[<span style="color: #6897bb;">1 </span>+ maxValue / <span style="color: #9876aa;">WORD_SIZE</span>]<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>logUsageInfo(maxValue)<span style="color: #cc7832;">;<br /></span>}<br /><br /><br /><span style="color: #cc7832;">public void </span><span style="color: #ffc66d;">set</span>(<span style="color: #cc7832;">int </span>v) {<br /> <span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">values</span>[slot(v)] |= position(v)<span style="color: #cc7832;">;<br /></span>}<br /><br /><span style="color: #cc7832;">public boolean </span><span style="color: #ffc66d;">check</span>(<span style="color: #cc7832;">int </span>v) {<br /> <span style="color: #cc7832;">int </span>value = <span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">values</span>[slot(v)] & position(v)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> return </span>value != <span style="color: #6897bb;">0</span><span style="color: #cc7832;">;<br /></span>}<br /><br /><span style="color: #cc7832;">public int </span><span style="color: #ffc66d;">position</span>(<span style="color: #cc7832;">int </span>v) {<br /> <span style="color: #cc7832;">return </span><span style="color: #6897bb;">1 </span><< (v & <span style="color: #9876aa;">WORD_SIZE </span>- <span style="color: #6897bb;">1</span>)<span style="color: #cc7832;">;<br /></span>}<br /><br /><span style="color: #cc7832;">public int </span><span style="color: #ffc66d;">slot</span>(<span style="color: #cc7832;">int </span>v) {<br /> <span style="color: #cc7832;">return </span>v >> <span style="color: #9876aa;">SLOT_SHIFT</span><span style="color: #cc7832;">;<br /></span>}</pre><p style="text-align: left;"><br /></p><p style="text-align: left;"><span style="font-family: verdana;">Whole pipeline will look something like below</span></p><p style="text-align: left;"><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQzJ_fKsmwGeTyPpBRYavifBGWWKKZ9kADEYlSMO-iUO5rSq4AgVa9MPRQpaA-BAOkrOcI-dqbJb_uSwpTgYwyJcs1u0FvKeBxF2Gwas3zKcpvPdDCET82IrbkX1Q1_aIN_ME33z98nr1l/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="264" data-original-width="573" height="294" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQzJ_fKsmwGeTyPpBRYavifBGWWKKZ9kADEYlSMO-iUO5rSq4AgVa9MPRQpaA-BAOkrOcI-dqbJb_uSwpTgYwyJcs1u0FvKeBxF2Gwas3zKcpvPdDCET82IrbkX1Q1_aIN_ME33z98nr1l/w640-h294/image.png" width="640" /></a></span></div><span style="font-family: verdana;"><br /></span><h3 style="text-align: left;"><span style="font-family: verdana;">Trade Off</span></h3><div><span style="font-family: verdana;"><br /></span></div><p></p><div><span style="font-family: verdana;">Nothing is perfect and this also has some constraints and it is good to be aware of it.</span></div><div><p></p><p><span style="font-family: verdana;">- This is a dense value data structure, so if we have to store a value that is of 100 Million then we have to allocate at least 100 million bits ( 95 MB). If values are sparse then find alternate data structure. </span></p><p><span style="font-family: verdana;">- Thread safety has to be handled at slot level because 32 values are packed in a single slot.</span></p><p><span style="font-family: verdana;">- Values should be distinct but if duplicate values are present and it is going to be less duplicates then additional data structures like maps can be used to keep frequency count. This needs to be handled in a little intelligent way like having some threshold on duplicate value and if it crosses that threshold then it is better to stop accepting value to avoid having everything going to map.</span></p><p><span style="font-family: verdana;">- Iteration. Since this is a compressed representation of dense value, iteration on available value has to be handled in a streaming approach to avoid allocation of huge in memory collection. One of the approach could be having API for consuming single value at a time and let client to decide on what to do with those values, example of such iteration could look something like below</span></p></div><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;"><span style="color: #cc7832;">public void </span><span style="color: #ffc66d;">consume</span>(IntConsumer consumer) {<br /> IntStream<br /> .<span style="font-style: italic;">range</span>(<span style="color: #6897bb;">1</span><span style="color: #cc7832;">, </span><span style="color: #9876aa;">maxValue</span>)<br /> .filter(<span style="color: #cc7832;">this</span>::check)<br /> .forEach(consumer::accept)<span style="color: #cc7832;">;<br /></span>}</pre><p style="text-align: left;"><span style="font-family: verdana;">- Range iteration. This data structure is very good for range query.</span></p><p style="text-align: left;"><span style="font-family: verdana;">- Compact set. This is also good DS for set related operations.</span></p><h2 style="text-align: left;"><span style="font-family: verdana;">Conclusion</span></h2><div><span style="font-family: verdana;">These are simple and yet </span><span style="font-family: verdana;">very powerful algorithms and if this fits the bill then it can be the difference between solving the problem or not solving at all.</span> </div>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com2tag:blogger.com,1999:blog-6543397255913469784.post-763041852651667762020-12-08T23:55:00.000-08:002020-12-08T23:55:40.854-08:00Disk storage algorithm<p><span style="font-family: verdana;"> This is follow up post on <a href="http://ashkrit.blogspot.com/2020/11/rethinking-key-value-store.html" target="_blank">rethinking-key-value-store</a> article to explore storage part of system.</span></p><p><br /></p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaQJva5StlEeap3v-BPl_h3UraoqOUrasIGjjGszlqwtxx6Y4k1Ov5mDAuujqCVgEToE1vRhkNfPtR3eEGpwzlZuZlaaWNrL78L2m1Hbsd3iOviirbCT9MCeFAOneWhh0G2lT7qLV79KMz/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="451" data-original-width="640" height="453" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaQJva5StlEeap3v-BPl_h3UraoqOUrasIGjjGszlqwtxx6Y4k1Ov5mDAuujqCVgEToE1vRhkNfPtR3eEGpwzlZuZlaaWNrL78L2m1Hbsd3iOviirbCT9MCeFAOneWhh0G2lT7qLV79KMz/w640-h453/image.png" width="640" /></a></div><br /><span style="font-family: verdana;">Many data systems support plugin based storage layers and it opens a whole set of options to use one from a shelf or build one that suits your needs.</span><p></p><p><span style="font-family: verdana;">In this post i will share how a new storage system can be built and later it is used for building time series application on top of it.</span></p><p><span style="font-family: verdana;">Before we go deep in disk based algorithm, let's look at why non-volatile storage is required.</span></p><p><span style="font-family: verdana;">In today times when machines with Terabytes RAM are available why do we have to bother to store stuff on disk ? </span></p><p><span style="font-family: verdana;">Couple of good reasons why still having good non-volatile storage manager makes sense today.</span></p><p></p><h3><ul style="text-align: left;"><li><span style="font-family: verdana;">It is cheap</span></li></ul></h3><span style="font-family: verdana;">Disk is very cheap as compared RAM, so it does not make sense to store data in expensive store especially when data is not being used frequently. Lots of cloud provider bill can saved! <br /></span><h3><ul style="text-align: left;"><li><span style="font-family: verdana;">It is unlimited</span></li></ul><p style="text-align: left;"><span style="font-weight: normal;"><span style="font-family: verdana; font-size: small;">Although a machine with big RAM is available but it is still limited , it will not continue get bigger at the same rate as in the past and if we want applications to have the illusion that it has unlimited memory then flushing to disk is a must. </span></span></p></h3><h3><ul style="text-align: left;"><li><span style="font-family: verdana;">Allow fast restarts</span></li></ul><span style="font-weight: normal;"><span style="font-family: verdana; font-size: small;">Think what will happen if application crash ? Application has to rebuild the whole state and it could take very long time before application is available again to serve request. Saving computed data to disk and restoring from it will be way faster.</span></span></h3><h3><ul style="text-align: left;"><li><span style="font-family: verdana;">Information exchange </span></li></ul></h3><p></p><p><span style="font-family: verdana;">How do two application running on different machine can communicate ? For inter application communication in-memory data has to written in wire format so that it can be sent over network.</span></p><p><span style="font-family: verdana;">and many more reasons..</span></p><p><span style="font-family: verdana;">Application has volatile & non-volatile area and storage manager sits in middle of that (.ie RAM and Disk) and provide efficient access to data.</span></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHiExYABbCvtJvdS-4g105boU3fqvUnXK-ztXPnsxC1uPEXeNY7oWVdmYDMNDiY30pOylmGts_Bk8trupylmQ1GAVeiMhxjpyUZ2Ns2pWvtihynK7jvA-RwoZRxqW479cVhO8tRV9ZtkY9/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="430" data-original-width="604" height="456" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHiExYABbCvtJvdS-4g105boU3fqvUnXK-ztXPnsxC1uPEXeNY7oWVdmYDMNDiY30pOylmGts_Bk8trupylmQ1GAVeiMhxjpyUZ2Ns2pWvtihynK7jvA-RwoZRxqW479cVhO8tRV9ZtkY9/w640-h456/image.png" width="640" /></a></div><br /><br /></div><span style="font-family: verdana;"><br /></span><p></p><p><span style="font-family: verdana;">RAM and DISK are very different types of hardware and access patterns are also very different.</span></p><p><span style="font-family: verdana;">On one hand RAM can be accessed <i>randomly </i>and it is <i>fast </i>for both read/write.</span></p><p><span style="font-family: verdana;">Disks are accessed <i>sequentially </i>using<i> blocks</i><i> </i>and very <i>slow </i>write and slow read, SSD has improved the access time but sequential access is the recommended to get best performance.</span></p><p><span style="font-family: verdana;">Storage managers have to use efficient data structure on disk to get best performance, another thing is that disk has nothing like <a href="https://draft.blogger.com/blog/post/edit/6543397255913469784/76304185265166776#" target="_blank">malloc</a> to manage allocation. Everything is bare metal and the application developer has to manage allocation, garbage collector, locks etc.</span></p><p><span style="font-family: verdana;">Disk read/write access is based on a block which is usually 4 KB, but memory read/write is based on a cacheline which is 64 Bytes, just this difference in read/write size requires new ways of organizing data to get the best out of the device. </span></p><p><span style="font-family: verdana;">All the above problems make writing disk based algorithms very challenging.</span></p><p><span style="font-family: verdana;">Lets look at some options of storing data on disk.</span></p><p><span style="font-family: verdana;">Generally file on disk looks some thing like below, each block is of fix sized and it depends on hardware, most of the vendors use 4 KB blocks. IO device provide atomic read/write guarantee at block level. </span></p><p><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8ITObEAkufJDbGc6jwlVQzyDMeidPVXr8IdogizlmiaHtDR_iuLgrlItEJuP4RdFn6iAPB-xPf4Q7ySBJK8YXxnzWDs_mi8LnqEiARV-avChVh8JVxe3vcDQWV47js0BxU009gvxsZHqm/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="124" data-original-width="346" height="115" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8ITObEAkufJDbGc6jwlVQzyDMeidPVXr8IdogizlmiaHtDR_iuLgrlItEJuP4RdFn6iAPB-xPf4Q7ySBJK8YXxnzWDs_mi8LnqEiARV-avChVh8JVxe3vcDQWV47js0BxU009gvxsZHqm/" width="320" /></a></span></div><span style="font-family: verdana;"><br /><br /></span><h2 style="text-align: left;"><span style="font-family: verdana;">Page Layout</span></h2><p></p><p><span style="font-family: verdana;">Lets unpack disk block to explore options to store data.</span></p><h3 style="text-align: left;"><span style="font-family: verdana;">Fixed Size</span></h3><div><span style="font-family: verdana;">Fixed size data is very common and intuitive way to store data in block provided underlying data is like that and mostly applicable for number variants data type like ( byte, short, int, long , float & double). It is possible to make it work for varchar but padding is required to achieve this. If underlying data can be mapped to fixed size value then this is best option.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbhO2Vouy4AaUVc1TzanOpTAR5RXyQiyUberleqveBcSmfmJ7A0UZtA8ZOhuHRSaA0BdxT9SiK2krXN9THctURaisdo1K0KHDDWC-jhIjZfKlxDUqNQ7d2SGA_-0OsxjrwKc744zVx8bCj/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="180" data-original-width="251" height="229" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbhO2Vouy4AaUVc1TzanOpTAR5RXyQiyUberleqveBcSmfmJ7A0UZtA8ZOhuHRSaA0BdxT9SiK2krXN9THctURaisdo1K0KHDDWC-jhIjZfKlxDUqNQ7d2SGA_-0OsxjrwKc744zVx8bCj/" width="320" /></a></div><br />Fixed size has good random access property, just by doing simple multiplication specific record can be found for eg to find 3rd record we will use <i>record * sizeof(record) (i.e 3 * 4)</i> to find the offset of data and read it. </span></div><div><span style="font-family: verdana;">Most of the application has variable record size due to which more flexible storage layout is required. </span></div><div><span style="font-family: verdana;"><br /></span></div><div><br /></div><h3><span style="font-family: verdana;">Size Prefix</span></h3><div><span style="font-family: verdana;">In this approach every record is prefixed with 4 Byte size and followed with data.</span></div><div><span style="font-family: verdana;">This has overhead of extra 4 Bytes and sometime this can be more than actual data and other thing that is not good is that it is sequential access, if last record is required then it requires to scan full page.</span></div><div><span style="font-family: verdana;">One more downsize is what happens when records are updated ? this will cause overflow or fragmentation. </span></div><div><span style="font-family: verdana;"><br /></span></div><p><span style="background-color: #f8f9fa; color: rgba(0, 0, 0, 0); font-family: monospace; font-size: 0px; white-space: nowrap;">%3CmxGraphModel%3E%3Croot%3E%3CmxCell%20id%3D%220%22%2F%3E%3CmxCell%20id%3D%221%22%20parent%3D%220%22%2F%3E%3CmxCell%20id%3D%222%22%20value%3D%22%22%20style%3D%22rounded%3D1%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3BfillColor%3D%23fff2cc%3BstrokeColor%3D%23d6b656%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22890%22%20y%3D%22385%22%20width%3D%22190%22%20height%3D%22130%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3C%2Froot%3E%3C%2FmxGraphModel%3E</span></p><p><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8IxoCGn2q2ZE2TPOQf3bU33PC4LXScV0d9kViEnE_4rR3wLRQ7wiUlwtfFwjuHlDlzW3yoAOCtQSRIeiViRzPWpTJt84xRgf3ad8ZjlaZRmOVXbTphrQxYRwND4wt4P9dPPx78vNyuv7y/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="152" data-original-width="286" height="170" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8IxoCGn2q2ZE2TPOQf3bU33PC4LXScV0d9kViEnE_4rR3wLRQ7wiUlwtfFwjuHlDlzW3yoAOCtQSRIeiViRzPWpTJt84xRgf3ad8ZjlaZRmOVXbTphrQxYRwND4wt4P9dPPx78vNyuv7y/" width="320" /></a></div><br /><p style="clear: both; text-align: left;"><br /><span style="font-family: verdana;">This is good for queue based system where write is always at the end and read is also large sequential scan. </span></p></div><p></p><h3><span style="font-family: verdana;">Slotted Page</span></h3><div><span style="font-family: verdana;">This approach is hybrid one and takes advantage of both fixed and size prefix page.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3smakkXCwkAeHPVc1Fkatl91gEIxjFTLti5Qn7kjz3M4flHfoVuwnCPfZai8OCjxbwB9Ry5SVZhKn2e4ae8CuootGJPYA2lioxM03nPcExO_H1MKSRCbFrYvTvz64wwpRdoH9OAGgeyFc/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="317" data-original-width="776" height="262" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3smakkXCwkAeHPVc1Fkatl91gEIxjFTLti5Qn7kjz3M4flHfoVuwnCPfZai8OCjxbwB9Ry5SVZhKn2e4ae8CuootGJPYA2lioxM03nPcExO_H1MKSRCbFrYvTvz64wwpRdoH9OAGgeyFc/w640-h262/image.png" width="640" /></a></div><br /><br /></div><br /></div><div class="separator" style="clear: both; text-align: left;">Slotted page is transformation of <b style="font-style: italic;">Size Prefix </b>page to co-locating related data, for eg all data is together and all size is together<b style="font-style: italic;">.</b></div><div class="separator" style="clear: both; text-align: left;"><b style="font-style: italic;"><br /></b></div><div class="separator" style="clear: both; text-align: left;">Single page contains 2 Region</div><div class="separator" style="clear: both; text-align: left;"><ul style="text-align: left;"><li><span style="font-family: verdana;"><div class="separator" style="clear: both; text-align: left;"><b>Header Region</b></div></span></li></ul><div class="gmail-separator" style="clear: both;">This section contains some metadata about the page that include version, page id , hash, number of records , compression flag etc. </div><div class="gmail-separator" style="clear: both;"> </div><ul style="text-align: left;"><li><div class="separator" style="clear: both; text-align: left;"><b>Data Region</b></div></li></ul><div class="gmail-separator" style="clear: both;">Data section is subdivided in <i>data segment</i> & <i>index segment</i>. Index segment is also called as <i>Slot Array </i>and it can be 4 byte or 2 byte fixed size value, it contains pointer to data segment.</div><div class="gmail-separator" style="clear: both;"><i>Data Segment </i>is written from left and <i>Slot Array </i>is written from right side. Page is considered full once no space is available for either <i>data segment </i>or <i>Slot.</i></div><div class="gmail-separator" style="clear: both;"><br /></div><div class="gmail-separator" style="clear: both;">This approach gives random access to records by using <i>Slot array, </i>every record can be addressed by (<i>PageId, Record Id). </i>Full file content can be seen as a 2 dimensional array.</div><div class="gmail-separator" style="clear: both;"></div></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiswDzwsk5xLcgNBaE5FQyNNq4VzZV-kpX11VAdgeCP3F42tMnr6YZFW7YjWbZZqnN12591MqlEdbiNW_8g9TkBIV2hyLgBSsRwwKGvK4CakmZljSrVX98LrigD50c-KvRhr562Hi0-5Bt1/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="202" data-original-width="386" height="209" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiswDzwsk5xLcgNBaE5FQyNNq4VzZV-kpX11VAdgeCP3F42tMnr6YZFW7YjWbZZqnN12591MqlEdbiNW_8g9TkBIV2hyLgBSsRwwKGvK4CakmZljSrVX98LrigD50c-KvRhr562Hi0-5Bt1/w400-h209/image.png" width="400" /></a></div><br /><div class="gmail-separator" style="clear: both;">Slotted page is a very popular layout for many databases. This also allows to build sparse or dense indexes based on page & slot.</div><div class="gmail-separator" style="clear: both;"><br /></div><div class="gmail-separator" style="clear: both;"><br /></div><h2 style="clear: both; text-align: left;">Disk Data Structure </h2><div><div class="gmail-separator" style="clear: both;">Now we will explore how smallest unit of storage (i.e. page) can be taken to build some data structures on top of it and finally some application using disk based data structure.</div><div class="gmail-separator" style="clear: both;"><br /></div></div><div class="gmail-separator" style="clear: both;">Remember disk has no malloc API, so we have to build something like <i>pagealloc </i>that will enable dynamic allocation of blocks/page.</div><div class="gmail-separator" style="clear: both;"><br /></div><div class="gmail-separator" style="clear: both;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqmFTUdytEydFuXldynMWqb2LUOkCjUGExrBs9tQhAXlah43NM0txC5GHyzueoeIsc6dWBHWXtQI3_WPx49aziUuHXmE0EhkUfekndBhQy1nhkcH7xfMy45s8b21fDJTDOsOhchEuYpw3c/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="468" data-original-width="707" height="424" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqmFTUdytEydFuXldynMWqb2LUOkCjUGExrBs9tQhAXlah43NM0txC5GHyzueoeIsc6dWBHWXtQI3_WPx49aziUuHXmE0EhkUfekndBhQy1nhkcH7xfMy45s8b21fDJTDOsOhchEuYpw3c/w640-h424/image.png" width="640" /></a></div><br />Page Allocator interface is an low level API and API looks something like below.</div><div class="gmail-separator" style="clear: both;"><br /></div><div class="gmail-separator" style="clear: both;"><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;"><span style="color: #cc7832;">public interface </span>PageAllocator {<br /> WritePage <span style="color: #ffc66d;">newPage</span>()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> long </span><span style="color: #ffc66d;">commit</span>(WritePage page)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span>ReadPage <span style="color: #ffc66d;">readByPageId</span>(<span style="color: #cc7832;">int </span>pageId)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> int </span><span style="color: #ffc66d;">noOfPages</span>()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> int </span><span style="color: #ffc66d;">pageSize</span>()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> byte </span><span style="color: #ffc66d;">version</span>()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span>List<PageInfo> <span style="color: #ffc66d;">pages</span>();<br /><br /> ReadPage <span style="color: #ffc66d;">readByPageOffset</span>(<span style="color: #cc7832;">long </span>offSet);<span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"><br /></span>}<br /></pre></div><div class="gmail-separator" style="clear: both;"><br /></div><h2 style="clear: both; text-align: left;">Application - Time Series Database</h2><div>Using Page allocator abstraction we will build <a href="https://en.wikipedia.org/wiki/Time_series_database" target="_blank">Time series database</a> that will use <i>Sorted String table </i>as underlying store.</div><div><br /></div><div>SSTable stores immutable rows that are ordered by some key in files. SSTable is basis for <a href="https://en.wikipedia.org/wiki/Log-structured_merge-tree" target="_blank">Log structured merge tree</a> that is alternative to B+Tree.</div><div><br /></div><div><a href="https://en.wikipedia.org/wiki/Log-structured_merge-tree" target="_blank">Log structured merge tree</a> powers many popular data stores like <a href="https://en.wikipedia.org/wiki/Bigtable" style="background: none rgb(255, 255, 255); color: #0b0080; font-family: sans-serif; font-size: 14px;" target="_blank">Bigtable</a><span style="background-color: white; color: #202122; font-family: sans-serif; font-size: 14px;">, </span><a class="mw-redirect" href="https://en.wikipedia.org/wiki/HBase" style="background: none rgb(255, 255, 255); color: #0b0080; font-family: sans-serif; font-size: 14px; text-decoration-line: none;">HBase</a><span style="background-color: white; color: #202122; font-family: sans-serif; font-size: 14px;">, </span><a href="https://en.wikipedia.org/wiki/LevelDB" style="background: none rgb(255, 255, 255); color: #0b0080; font-family: sans-serif; font-size: 14px; text-decoration-line: none;">LevelDB</a><span style="background-color: white; color: #202122; font-family: sans-serif; font-size: 14px;">, </span><span style="background-color: white; color: #202122; font-family: sans-serif; font-size: 14px;"> </span><a href="https://en.wikipedia.org/wiki/RocksDB" style="background: none rgb(255, 255, 255); color: #0b0080; font-family: sans-serif; font-size: 14px; text-decoration-line: none;">RocksDB</a><span style="background-color: white; color: #202122; font-family: sans-serif; font-size: 14px;">, </span><a href="https://en.wikipedia.org/wiki/WiredTiger" style="background: none rgb(255, 255, 255); color: #0b0080; font-family: sans-serif; font-size: 14px; text-decoration-line: none;">WiredTiger</a><span style="background-color: white; color: #202122; font-family: sans-serif; font-size: 14px;">,</span><span style="background-color: white; color: #202122; font-family: sans-serif; font-size: 14px;"> </span><a href="https://en.wikipedia.org/wiki/Apache_Cassandra" style="background: none rgb(255, 255, 255); color: #0b0080; font-family: sans-serif; font-size: 14px; text-decoration-line: none;" target="_blank">Cassandra</a><span style="background-color: white; color: #202122; font-family: sans-serif; font-size: 14px;">, </span><a href="https://en.wikipedia.org/wiki/InfluxDB" style="background: none rgb(255, 255, 255); color: #0b0080; font-family: sans-serif; font-size: 14px; text-decoration-line: none;">InfluxDB</a><span style="background-color: white; color: #202122; font-family: sans-serif; font-size: 14px;"> and many more.</span></div><div><br /></div><div><br /></div><h3 style="text-align: left;">SSTable</h3><div>SSTable is made of couple of ordered memory maps & ordered rows on disk. Storage manage sits right in middle to manage sorted structures on disk & memory.</div><div><br /></div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQ-oF4YbiKsqpBOAQw79mwCvCm2Q26l8KaXqArSsoUbNBPVifSNjslLZ2iuYdaJER0LxiGzwY5LEObk4xpo_82bXe0sIMrogKVk9UlUCTLmv2yo3rnqLK44OagZlYm1caajKg8KtIwpbIH/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="430" data-original-width="834" height="330" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQ-oF4YbiKsqpBOAQw79mwCvCm2Q26l8KaXqArSsoUbNBPVifSNjslLZ2iuYdaJER0LxiGzwY5LEObk4xpo_82bXe0sIMrogKVk9UlUCTLmv2yo3rnqLK44OagZlYm1caajKg8KtIwpbIH/w640-h330/image.png" width="640" /></a></div><br /><br /></div><h4 style="text-align: left;"><u>Writers</u> </h4><div><div>All the write requests are handled by writing to <i>In-Memory ordered map</i> and once those maps are full then get converted to <i>read only In-Memory maps </i>and periodically flushed to disk for durability. </div><div><br /></div><div>Writing to such a system is very fast because it is done using in-memory data structure. </div></div><div><br /></div><div><h4><u>Readers</u></h4></div><div><div><div>Readers is where this gets more interesting because now read has to hit multiple data structures to find records. First it scans mutable map, then immutable maps and finally on the disk.</div><div>Rather than doing a single seek it has to do multiple seeks but since all the data structure be on memory or disk is sorted, so requests can be processed in LOG N time.</div></div><div><br /></div><div>Over a period of time a number of files can grow, so a compaction process is required that will merge multiple sorted files and create a small number of files. This compaction process is what makes SSTable as Log structured merge tree.</div></div><div><br /></div><div>Some code !</div><div>To have some thing working i used <a href="https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentSkipListMap.html" target="_blank">ConcurrentSkipListMap</a> from JDK to create In-Memory ordered map and use PageAllocator to flush ordered map to disk.</div><div><br /></div><div>SortedStringTable</div><div><br /></div><div><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;"><span style="color: #cc7832;">public interface </span>SortedStringTable<<span style="color: #507874;">V</span>> {<br /><br /> <span style="color: #cc7832;">void </span><span style="color: #ffc66d;">append</span>(String key<span style="color: #cc7832;">, </span><span style="color: #507874;">V </span>value)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> void </span><span style="color: #ffc66d;">iterate</span>(String from<span style="color: #cc7832;">, </span>String to<span style="color: #cc7832;">, </span>Function<<span style="color: #507874;">V</span><span style="color: #cc7832;">, </span>Boolean> consumer)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span><span style="color: grey;">// API for saving SST table for persistence storage<br /></span><span style="color: grey;"> </span>Collection<PageRecord<<span style="color: #507874;">V</span>>> <span style="color: #ffc66d;">buffers</span>()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> void </span><span style="color: #ffc66d;">remove</span>(<span style="color: #cc7832;">int </span>pageId)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> void </span><span style="color: #ffc66d;">flush</span>()<span style="color: #cc7832;">;<br /></span>}</pre></div><div>Working SSTable code can be found @ <a href="https://github.com/ashkrit/corejava/tree/master/query/src/main/java/query/timeseries/sst" target="_blank">sst</a> github.</div><div><br /></div><div><br /></div><div>First data structure is ready for our time series database :-)</div><div><br /></div><h3 style="text-align: left;">Time Series </h3><div>Time series application will be built on top of SSTable.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8q60YYMwF-lqWxzX6tk5ty3bD6IeOGrSelz75Vou4QR-42gM1rqwF33cwn5hvnlon_g5oMr8gPCsaG-SN60ltZ1Yd3of4ue9Csi4G3omswVRp4_fa156Z64Vi4epxRsVLKfyRsa8IPjVd/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="239" data-original-width="340" height="281" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8q60YYMwF-lqWxzX6tk5ty3bD6IeOGrSelz75Vou4QR-42gM1rqwF33cwn5hvnlon_g5oMr8gPCsaG-SN60ltZ1Yd3of4ue9Csi4G3omswVRp4_fa156Z64Vi4epxRsVLKfyRsa8IPjVd/w400-h281/image.png" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Timeseries interface is simple, it looks something like below.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;"><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;"><span style="color: #cc7832;">public interface </span>TimeSeriesStore {<br /><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span><<span style="color: #507874;">T</span>> EventInfo <span style="color: #ffc66d;">insert</span>(<span style="color: #507874;">T </span>row)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> void </span><span style="color: #ffc66d;">gt</span>(LocalDateTime fromTime<span style="color: #cc7832;">, </span>Function<EventInfo<span style="color: #cc7832;">, </span>Boolean> consumer)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> void </span><span style="color: #ffc66d;">lt</span>(LocalDateTime toTime<span style="color: #cc7832;">, </span>Function<EventInfo<span style="color: #cc7832;">, </span>Boolean> consumer)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> void </span><span style="color: #ffc66d;">between</span>(LocalDateTime startTime<span style="color: #cc7832;">, </span>LocalDateTime endTime<span style="color: #cc7832;">, </span>Function<EventInfo<span style="color: #cc7832;">, </span>Boolean> consumer)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> void </span><span style="color: #ffc66d;">flush</span>();<br /><br /> <br />}</pre></div>Time series application code can be found @ <a href="https://github.com/ashkrit/corejava/blob/master/query/src/main/java/query/timeseries" target="_blank">timeseries</a> repo.</div><div><br /></div><div>To experiment with some some real time series data, i picked up sample data from <a href="https://www1.nyc.gov/site/tlc/about/tlc-trip-record-data.page" target="_blank">Jan Yellow Taxi Trip</a> and loaded in the app. <a href="https://s3.amazonaws.com/nyc-tlc/trip+data/yellow_tripdata_2020-01.csv" target="_blank">yellow_tripdata_2020-01</a> has 6+ Million records.</div><div><br /></div><div>Sample time series application using this data can be found @ <a href="https://github.com/ashkrit/corejava/blob/master/query/src/main/java/query/app/timeseries/NYTaxiRides.java" target="_blank">NYTaxiRides.java</a></div><div><br /></div><div>All the code has good unit test coverage, so feel free to hack and learn.</div><div><br /></div><div><h2 style="text-align: left;">Conclusion</h2><div>Disk based algorithm are very cool and understanding it gives good idea about how modern data systems work. You might not build data system from scratch but knowing these algorithm will definitely help in deciding which data system to pick based on trade off.</div></div></div></span></div>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com0tag:blogger.com,1999:blog-6543397255913469784.post-66916649292078575382020-11-16T01:13:00.000-08:002020-11-16T01:13:41.095-08:00Rethinking key value store<p><span style="font-family: verdana;"><span>In this post i will share how key-value stores can be used for building different types of application, but before we start deep dive lets do quick recap of database systems.</span></span></p><p><span style="font-family: verdana;"><span>Database technology is made of 2 important components ( Storage + Query). Such type of </span>architecture allow to replace part of system or only build storage or query part of it and reuse other from open source.</span></p><p><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUp0dnQljMd-HfvG0xeWvpBw5esX1IGWosvJPPZOZJSONxCYicqZJCRorHD2LgOpuI_Go0yFb_dvJqWTvZ9HFWHRZIRlbL75oCcCvFmrNfEfcZLrH7Z0u3aJiJsJcM4Du0AextxEhHu9sK/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="512" data-original-width="726" height="452" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUp0dnQljMd-HfvG0xeWvpBw5esX1IGWosvJPPZOZJSONxCYicqZJCRorHD2LgOpuI_Go0yFb_dvJqWTvZ9HFWHRZIRlbL75oCcCvFmrNfEfcZLrH7Z0u3aJiJsJcM4Du0AextxEhHu9sK/w640-h452/image.png" width="640" /></a></span></div><span style="font-family: verdana;"><br /><br /></span></div><span style="font-family: verdana;"><br />Many modern database are taking advantage of plugin architecture and focusing on only one part of system, for example storage could be again broken down in various format based on access pattern for reads/write</span><p></p><p><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7TFguobpLqbJbDpzXD0GPFdOIausebFiW4Oh3GIjhl90TDO9iHxQuAsVnhQ5Y4c5ntXfh3QmRY-AWkUvh9w8mDfAUtQsk3-QcUe3EypTvVWx97nlb2v7gD8PvzHEsdrdeHcm_xtexbgl0/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="338" data-original-width="671" height="322" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7TFguobpLqbJbDpzXD0GPFdOIausebFiW4Oh3GIjhl90TDO9iHxQuAsVnhQ5Y4c5ntXfh3QmRY-AWkUvh9w8mDfAUtQsk3-QcUe3EypTvVWx97nlb2v7gD8PvzHEsdrdeHcm_xtexbgl0/w640-h322/image.png" width="640" /></a></span></div><span style="font-family: verdana;"><br /> </span><p></p><p><span style="font-family: verdana;"><span>Each storage format ( hash,b-tree,lsm or log) is optimized for specific read/write access pattern. Many database uses multiples format also.</span></span></p><p><span style="font-family: verdana;"><span>Once we come to storage format then <b><i>Row </i></b>vs <b><i>Column </i></b>layout also comes in play, so in nutshell it is hard to build storage system, so it makes sense to use off the self solution rather than building something from ground up.</span></span></p><p><span style="font-family: verdana;"><span>Query area is also huge as this include different types of API, Query engine, Optimizer , transactions etc</span></span></p><p><span style="font-family: verdana;"><span></span></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><span><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgX_4TIqLxtZB_CphbCkcAqO2KDl4qTuW7D4PsBnKLHyHHp0chfqDmsMSZNvjfLEwaZGkj1kJoFANGkPkahqkVZSTQOMMcSX4WPpw2CyC4a0U2haqevX9mPGPh4v2us1u1qbP0TWuoNXDhG/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="340" data-original-width="617" height="352" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgX_4TIqLxtZB_CphbCkcAqO2KDl4qTuW7D4PsBnKLHyHHp0chfqDmsMSZNvjfLEwaZGkj1kJoFANGkPkahqkVZSTQOMMcSX4WPpw2CyC4a0U2haqevX9mPGPh4v2us1u1qbP0TWuoNXDhG/w640-h352/image.png" width="640" /></a></span></span></div><span style="font-family: verdana;"><span><br /><br /></span></span><p></p><p><span style="background-color: #f8f9fa; color: rgba(0, 0, 0, 0); font-family: monospace; font-size: 0px; white-space: nowrap;">%3CmxGraphModel%3E%3Croot%3E%3CmxCell%20id%3D%220%22%2F%3E%3CmxCell%20id%3D%221%22%20parent%3D%220%22%2F%3E%3CmxCell%20id%3D%222%22%20style%3D%22edgeStyle%3Dnone%3Brounded%3D1%3BorthogonalLoop%3D1%3BjettySize%3Dauto%3Bhtml%3D1%3BentryX%3D0.5%3BentryY%3D0%3BentryDx%3D0%3BentryDy%3D0%3B%22%20edge%3D%221%22%20source%3D%224%22%20target%3D%228%22%20parent%3D%221%22%3E%3CmxGeometry%20relative%3D%221%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%223%22%20style%3D%22edgeStyle%3Dnone%3Brounded%3D1%3BorthogonalLoop%3D1%3BjettySize%3Dauto%3Bhtml%3D1%3BentryX%3D0.5%3BentryY%3D0%3BentryDx%3D0%3BentryDy%3D0%3B%22%20edge%3D%221%22%20source%3D%224%22%20target%3D%229%22%20parent%3D%221%22%3E%3CmxGeometry%20relative%3D%221%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%224%22%20value%3D%22Storage%22%20style%3D%22rounded%3D1%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3BfillColor%3D%23fff2cc%3BstrokeColor%3D%23d6b656%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22320%22%20y%3D%22270%22%20width%3D%22200%22%20height%3D%2240%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%225%22%20style%3D%22rounded%3D1%3BorthogonalLoop%3D1%3BjettySize%3Dauto%3Bhtml%3D1%3B%22%20edge%3D%221%22%20source%3D%228%22%20target%3D%2210%22%20parent%3D%221%22%3E%3CmxGeometry%20relative%3D%221%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%226%22%20value%3D%22%22%20style%3D%22rounded%3D0%3BorthogonalLoop%3D1%3BjettySize%3Dauto%3Bhtml%3D1%3B%22%20edge%3D%221%22%20source%3D%228%22%20target%3D%2211%22%20parent%3D%221%22%3E%3CmxGeometry%20relative%3D%221%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%227%22%20style%3D%22rounded%3D0%3BorthogonalLoop%3D1%3BjettySize%3Dauto%3Bhtml%3D1%3BentryX%3D0.342%3BentryY%3D-0.033%3BentryDx%3D0%3BentryDy%3D0%3BentryPerimeter%3D0%3B%22%20edge%3D%221%22%20source%3D%228%22%20target%3D%2212%22%20parent%3D%221%22%3E%3CmxGeometry%20relative%3D%221%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%228%22%20value%3D%22Disk%22%20style%3D%22rounded%3D1%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3BfillColor%3D%23f8cecc%3BstrokeColor%3D%23b85450%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22250%22%20y%3D%22360%22%20width%3D%22140%22%20height%3D%2230%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%229%22%20value%3D%22Memory%22%20style%3D%22rounded%3D1%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3BfillColor%3D%23e1d5e7%3BstrokeColor%3D%239673a6%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22480%22%20y%3D%22360%22%20width%3D%22120%22%20height%3D%2235%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2210%22%20value%3D%22Flash%2FSSD%22%20style%3D%22rounded%3D1%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3BfillColor%3D%23f8cecc%3BstrokeColor%3D%23b85450%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%2290%22%20y%3D%22440%22%20width%3D%22120%22%20height%3D%2230%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2211%22%20value%3D%22HDD%22%20style%3D%22rounded%3D1%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3BfillColor%3D%23f8cecc%3BstrokeColor%3D%23b85450%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22240%22%20y%3D%22450%22%20width%3D%22120%22%20height%3D%2230%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2212%22%20value%3D%22Cloud%22%20style%3D%22rounded%3D1%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3BfillColor%3D%23f8cecc%3BstrokeColor%3D%23b85450%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22370%22%20y%3D%22480%22%20width%3D%22120%22%20height%3D%2230%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2213%22%20style%3D%22edgeStyle%3Dnone%3Brounded%3D1%3BorthogonalLoop%3D1%3BjettySize%3Dauto%3Bhtml%3D1%3BentryX%3D0.5%3BentryY%3D0%3BentryDx%3D0%3BentryDy%3D0%3B%22%20edge%3D%221%22%20source%3D%2214%22%20target%3D%224%22%20parent%3D%221%22%3E%3CmxGeometry%20relative%3D%221%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2214%22%20value%3D%22API%22%20style%3D%22rounded%3D1%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3BfillColor%3D%23dae8fc%3BstrokeColor%3D%236c8ebf%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22320%22%20y%3D%22190%22%20width%3D%22200%22%20height%3D%2240%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2215%22%20style%3D%22edgeStyle%3Dnone%3Brounded%3D1%3BorthogonalLoop%3D1%3BjettySize%3Dauto%3Bhtml%3D1%3BentryX%3D0.5%3BentryY%3D0%3BentryDx%3D0%3BentryDy%3D0%3B%22%20edge%3D%221%22%20source%3D%2216%22%20target%3D%2214%22%20parent%3D%221%22%3E%3CmxGeometry%20relative%3D%221%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2216%22%20value%3D%22SQL%22%20style%3D%22rounded%3D1%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3BfillColor%3D%2360a917%3BstrokeColor%3D%232D7600%3BfontColor%3D%23ffffff%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22260%22%20y%3D%22110%22%20width%3D%22120%22%20height%3D%2230%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2217%22%20style%3D%22edgeStyle%3Dnone%3Brounded%3D1%3BorthogonalLoop%3D1%3BjettySize%3Dauto%3Bhtml%3D1%3BentryX%3D0.5%3BentryY%3D0%3BentryDx%3D0%3BentryDy%3D0%3B%22%20edge%3D%221%22%20source%3D%2218%22%20target%3D%2214%22%20parent%3D%221%22%3E%3CmxGeometry%20relative%3D%221%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2218%22%20value%3D%22Key%20Value%22%20style%3D%22rounded%3D1%3BwhiteSpace%3Dwrap%3Bhtml%3D1%3BfillColor%3D%231ba1e2%3BstrokeColor%3D%23006EAF%3BfontColor%3D%23ffffff%3B%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22450%22%20y%3D%22110%22%20width%3D%22120%22%20height%3D%2230%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3CmxCell%20id%3D%2219%22%20value%3D%22Database%20Engine%22%20style%3D%22text%3Bhtml%3D1%3BstrokeColor%3Dnone%3BfillColor%3Dnone%3Balign%3Dcenter%3BverticalAlign%3Dmiddle%3BwhiteSpace%3Dwrap%3Brounded%3D0%3BfontFamily%3DVerdana%3BfontSize%3D18%3BfontStyle%3D1%22%20vertex%3D%221%22%20parent%3D%221%22%3E%3CmxGeometry%20x%3D%22320%22%20y%3D%2230%22%20width%3D%22200%22%20height%3D%2240%22%20as%3D%22geometry%22%2F%3E%3C%2FmxCell%3E%3C%2Froot%3E%3C%2FmxGraphModel%3E</span></p><p><span style="font-family: verdana;">With some high level understanding of databases now we can now focus on specific type of storage system and try to explore what types of application can be built on it.</span></p><p><span style="font-family: verdana;">Key-Value store is very popular storage system options. </span></p><p><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmMuvo5e0hDTgSczSdpQnrkhM7Uc-V8_-huInRVqJWKsFYN9YHGOw24AfnOMLRf_Gky0uPk4EfYjq1pu6mefY3TrjdFyG2VFnTwkrvd-FIMOy7F45fC8cDuK7eyGMjUy904xoPLrgZ7s-B/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="170" data-original-width="351" height="155" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmMuvo5e0hDTgSczSdpQnrkhM7Uc-V8_-huInRVqJWKsFYN9YHGOw24AfnOMLRf_Gky0uPk4EfYjq1pu6mefY3TrjdFyG2VFnTwkrvd-FIMOy7F45fC8cDuK7eyGMjUy904xoPLrgZ7s-B/" width="320" /></a></div><br /><br /></div><span style="font-family: verdana;"><br />Key value store are like hash table that is designed to store/retrieve key & value tuple. Key-Value store can be memory or disk based. Some key value stores variations like B-Tree or LSM tree are ordered key value store.</span><div><span style="font-family: verdana;">Ordered KV stores provide efficient range scan and is very important if client interface is SQL like.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">I will used ordered KV store as example to share type of application that can be build. I will use ecommerce Order data model as example for all the usecase</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYLXlT7cHcGByheXoHxfIRIwCcIh4QeJhOOhrwacGVOF6PcMSSHozt8efMVrrkQT4DexvO8Ga99drzGQ7eo1tn7xESldwkqnL8xk6XPmA3v0-qNhzWgo4-L8WkFvrIMxWpPWbzFh8lnUuy/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="238" data-original-width="201" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYLXlT7cHcGByheXoHxfIRIwCcIh4QeJhOOhrwacGVOF6PcMSSHozt8efMVrrkQT4DexvO8Ga99drzGQ7eo1tn7xESldwkqnL8xk6XPmA3v0-qNhzWgo4-L8WkFvrIMxWpPWbzFh8lnUuy/" width="203" /></a></div><br /><br /></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana; font-size: large;">- Row Store</span></div><div><span style="font-family: verdana;"> This is the most straight forward use-case for any KV stores and also very good fit for building unique index for primary key. For order table KV store will contain below entry</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><i>Order/OrderId/1 -> {......}</i></span></div><div><span style="font-family: verdana;"><i>Order/OrderId/2 -> {......}</i></span></div><div><span style="font-family: verdana;"><i>Order/OrderId/3 -> {......}</i></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Important thing to note in this format is that it contains fully qualified id of record that us made up of <i>{table name}/{PK Column}/{PK Value}</i>.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">This type of key provides flexibility to use single KV store for multiple tables, another advantage that we get it since all the values are ordered by Key so query has to load less numbers of pages to get all the data for this table.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">- </span><span style="font-family: verdana; font-size: x-large;">Indexes</span></div><div><span style="font-family: verdana;">Ordered KV store can be extended to build non unique indexes also so that it can be used as efficient lookup table for underlying data.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Lets try to build couple of non unique index on order table.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><b>Index on customer id </b></span></div><div><span style="font-family: verdana;"><br /></span></div><div><div><span style="font-family: verdana;"><i>Order/CustomerId/100/1 -> {......}</i></span></div><div><span style="font-family: verdana;"><i>Order/CustomerId/101/1 -> {......}</i></span></div><div><span style="font-family: verdana;"><i>Order/CustomerId/102/2 -> {......}</i></span></div></div><div><span style="font-family: verdana;"><br /></span><div><span style="font-family: verdana;"><b>Index on Order Date</b></span></div></div><div><span style="font-family: verdana;"><br /></span></div><div><span><div style="font-family: "Times New Roman";"><span style="font-family: verdana;"><i>Order/OrderDate/20201001/1 -> {......}</i></span></div><div style="font-family: "Times New Roman";"><span style="font-family: verdana;"><i>Order/</i><i>OrderDate</i><i>/</i><i>20201001</i><i>/2 -> {......}</i></span></div><div style="font-family: "Times New Roman";"><span style="font-family: verdana;"><i>Order/</i><i>OrderDate</i><i>/</i><i>20201010</i><i>/3 -> {......}</i></span></div><div style="font-family: "Times New Roman";"><span style="font-family: verdana;"><i><br /></i></span></div><div style="font-family: verdana;">Before i show other complex example lets try to understand format of key, it is contains "{table name}/{Index Col}/{Index Value}/{PK}" pattern, this pattern allow to handle duplicate values in index because primary key is suffix of every entry.</div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;">Lets keep going on this with little more complex example of index by Status & Order date</div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;"><div style="font-family: "Times New Roman";"><i style="font-family: verdana;">Order/</i><i style="font-family: verdana;">StatusByOrderDate</i><i style="font-family: verdana;">/CANCELLED_</i><i style="font-family: verdana;">20201010</i><i style="font-family: verdana;">/3 -> {......}</i></div><div style="font-family: "Times New Roman";"><i style="font-family: verdana;">Order/</i><i style="font-family: verdana;">StatusByOrderDate</i><i style="font-family: verdana;">/CANCELLED_</i><i style="font-family: verdana;">20201010</i><i style="font-family: verdana;">/6 -> {......}</i></div><div style="font-family: "Times New Roman";"><span style="font-family: verdana;"><i>Order/</i><i>StatusByOrderDate</i><i>/PENDING_</i><i>20201001</i><i>/2 -> {......}</i></span></div><div style="font-family: "Times New Roman";"><span style="font-family: verdana;"><i>Order/</i><i>StatusByOrderDate</i><i>/PENDING_</i><i>20201001</i><i>/7 -> {......}</i></span></div><div style="font-family: "Times New Roman";"><span style="font-family: verdana;"><i>Order/</i><i>StatusByOrderDate</i><i>/PENDING_</i><i>20201003</i><i>/8 -> {......}</i></span></div></div><div style="font-family: verdana;"><i>Order/StatusByOrderDate/SHIPPED_20201001/1 -> {......}</i></div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;">In above example Index key is made of 2 column.</div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;"><span style="font-family: verdana;">- <span style="font-size: large;">Range Scans</span></span></div><div style="font-family: verdana;"><span style="font-family: verdana;"><span style="font-size: large;"><br /></span></span></div><div style="font-family: verdana;"><span style="font-family: verdana;">By this point it will be becoming clear how efficient range scan can be done with above index data.</span></div><div style="font-family: verdana;"><span style="font-family: verdana;"><br /></span></div><div style="font-family: verdana;"><span style="font-family: verdana;">Lets use some example query to verify this .</span></div><div style="font-family: verdana;"><span style="font-family: verdana;"><br /></span></div><div style="font-family: verdana;"><span style="font-family: verdana;"> - <b>find all the orders that are cancelled between 20201001 to 20201015.</b></span></div><div style="font-family: verdana;">Above query can be resolved by looking for all the records between <i>Order/</i><i>StatusByOrderDate</i><i>/CANCELLED_</i><i>20201001 to </i><i>Order/</i><i>StatusByOrderDate</i><i>/CANCELLED_</i><i>20201031</i></div><div style="font-family: verdana;"><span style="font-family: verdana;"><br /></span></div><div style="font-family: verdana;">- <b>find all the orders that are cancelled in Oct 2020.</b></div><div style="font-family: verdana;">All records matching <i>Order/StatusByOrderDate/CANCELLED_202010</i> pattern</div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;"><b>-find all the pending orders</b></div><div style="font-family: verdana;">All records matching <i>Order/StatusByOrderDate/PENDING</i> pattern</div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;">One thing to note that all the related keys are together and can be retrieved using less number of IO operations. </div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;"><span style="font-size: large;">- Partition Index</span></div><div style="font-family: verdana;">Distributed databases creates partitions to split big data into chunk of manageable data so that it can be distributed and replicated.</div><div style="font-family: verdana;">Partition can be done using hash key or by key range. Key range based partition are must of interface is SQL or allows range scanning. </div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGBEleB40IZx7L4Vv2l0KaIc5D5NGv5Bsc33Xx3wd-oif9xGmZ_rKVZBaKeyLLqKYPRinIF-vuqJWJkDF7XpiC19l5GncQH2TEjkgdRJPppalTbUYpQOAHxiNl91rEZBhz-x5NfFEwwwjT/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="851" data-original-width="2147" height="254" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGBEleB40IZx7L4Vv2l0KaIc5D5NGv5Bsc33Xx3wd-oif9xGmZ_rKVZBaKeyLLqKYPRinIF-vuqJWJkDF7XpiC19l5GncQH2TEjkgdRJPppalTbUYpQOAHxiNl91rEZBhz-x5NfFEwwwjT/w640-h254/image.png" width="640" /></a></div><br />Lets take above data for key column as example set to distributed. </div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;">One way to create partition is to create partition of size 5 and store start & end key of that partition with data node reference, so that query looking for data in that range can be quickly resolved.</div><div style="font-family: verdana;"><br /></div><div><div ccp_infra_copy_id="5a4a0be3-fa7d-4f26-925b-51e4f35284a6" ccp_infra_timestamp="1605511341363" ccp_infra_user_hash="2599863757" ccp_infra_version="3" data-ccp-timestamp="1605511341363"><span style="font-family: verdana;">Partition index might look something like below.</span></div><div ccp_infra_copy_id="5a4a0be3-fa7d-4f26-925b-51e4f35284a6" ccp_infra_timestamp="1605511341363" ccp_infra_user_hash="2599863757" ccp_infra_version="3" data-ccp-timestamp="1605511341363"><span style="font-family: verdana;"><br /></span></div><div ccp_infra_copy_id="5a4a0be3-fa7d-4f26-925b-51e4f35284a6" ccp_infra_timestamp="1605511341363" ccp_infra_user_hash="2599863757" ccp_infra_version="3" data-ccp-timestamp="1605511341363"><span style="font-family: verdana;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidrU8zfr5Gcrq_hhWGpHJIrkDh7kKKBuKSMXZ66Uir2V3kGqvkUBs5mYsM26i84Mo4FVbW1UxZT6HoE36iigWQXX5P3tSpbA5Ru9jObrw817V-81491AO1DyfeEUqWoZWi7cDuy-bNldtF/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="613" data-original-width="927" height="424" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidrU8zfr5Gcrq_hhWGpHJIrkDh7kKKBuKSMXZ66Uir2V3kGqvkUBs5mYsM26i84Mo4FVbW1UxZT6HoE36iigWQXX5P3tSpbA5Ru9jObrw817V-81491AO1DyfeEUqWoZWi7cDuy-bNldtF/w640-h424/image.png" width="640" /></a></div><br /><br /></div><span style="font-size: large;">- Column Store</span></span></div><div ccp_infra_copy_id="5a4a0be3-fa7d-4f26-925b-51e4f35284a6" ccp_infra_timestamp="1605511341363" ccp_infra_user_hash="2599863757" ccp_infra_version="3" data-ccp-timestamp="1605511341363"><span style="font-family: verdana;">One more place where ordered KV stores shines is creating column store.</span></div><div ccp_infra_copy_id="5a4a0be3-fa7d-4f26-925b-51e4f35284a6" ccp_infra_timestamp="1605511341363" ccp_infra_user_hash="2599863757" ccp_infra_version="3" data-ccp-timestamp="1605511341363"><span style="font-family: verdana;"><br /></span></div><div ccp_infra_copy_id="5a4a0be3-fa7d-4f26-925b-51e4f35284a6" ccp_infra_timestamp="1605511341363" ccp_infra_user_hash="2599863757" ccp_infra_version="3" data-ccp-timestamp="1605511341363"><span style="font-family: verdana;">We need slight change in format to achieve column store, it will use <i>{table name}/{column}/{pk} -> {column value} </i>format<br /><br /></span></div></div><div style="font-family: verdana;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieiHb5VgYuqeHsEMztxzEAC0l6iSyBPDPjLCOoblu8O5NqE3DygGtwCPCO6RJ6CQVcusrzMJSHWwyksmlpkxPsgBfT4ung74yF7wdX-b0PnLKXp7Rj7icKt-w-lKqsBvBd1afFgvPKtfC5/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="954" data-original-width="1218" height="502" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieiHb5VgYuqeHsEMztxzEAC0l6iSyBPDPjLCOoblu8O5NqE3DygGtwCPCO6RJ6CQVcusrzMJSHWwyksmlpkxPsgBfT4ung74yF7wdX-b0PnLKXp7Rj7icKt-w-lKqsBvBd1afFgvPKtfC5/w640-h502/image.png" width="640" /></a></div><br />This type of key format will put all the column for given table together.</div><div style="font-family: verdana;">Any query that needs selective column can take advantage of this layout as all the column are next to each other.</div><div style="font-family: verdana;"> </div><div style="font-family: verdana;">- <span style="font-size: large;">SQL</span></div><div style="font-family: verdana;">Last one is building SQL interface on ordered map! SQL query need 2 basic operation to answer all the request</div><div style="font-family: verdana;"> - Point lookup</div><div style="font-family: verdana;"> - Range scan</div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;">With above examples of KV stores it is easy to build simple SQL using above mention access pattern.</div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;"><span style="font-size: x-large;">Sample Application</span></div><div style="font-family: verdana;">As part of the research for this post, i build implementation that is based on all the ideas shared above. High level architecture of simple implementation looks something like below</div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYYd8TqBNAV-uxMbBdW9j06V8bJrOn9f8O5s-CnUFPl9HCYRqN8pcTsJ1TcDczUQZEYhPHj2s_nJw7dtohp1X5qXKKhTnCJdlfQZYZDeBjHQa79kX7U8tySTY9x1dOLKtf4ta-bPHvNmV0/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="364" data-original-width="639" height="364" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYYd8TqBNAV-uxMbBdW9j06V8bJrOn9f8O5s-CnUFPl9HCYRqN8pcTsJ1TcDczUQZEYhPHj2s_nJw7dtohp1X5qXKKhTnCJdlfQZYZDeBjHQa79kX7U8tySTY9x1dOLKtf4ta-bPHvNmV0/w640-h364/image.png" width="640" /></a></div><br /><br /></div><div style="font-family: verdana;">In this application i have used 3 implementation of Ordered map to show underlying storage can be changed without given up on any functionality. </div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;"><span style="font-size: medium;">- SkipList </span></div><div style="font-family: verdana;">This is java "ConcurrentSkipListMap", which is in memory ordered map and is used in many real open source database to build memory store on top of LSM tree. Cassandra uses SkipList data structure for Memory table.</div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;"><span style="font-size: large;">- B Tree</span></div><div style="font-family: verdana;">H2 database is based on B-Tree storage called <i style="font-weight: bold;">"MVStore", </i>it is possible to use MvStore as library. I used MVStore as B-Tree storage</div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;"><span style="font-size: large;">- LSM Tree</span></div><div style="font-family: verdana;">Many implementation are available and popular ones are LevelDB, RocksDB etc. I used RocksDB as it is already used in many databases as storage engine. MyRocks, CassandraRocks, cockroachdb etc</div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;">Implementation is based on 2 interface ( KeyValueStore & SSTable) to expose both key value and SQL interface on different storage backend.</div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;"><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;"><span style="color: #cc7832;">public interface </span>KeyValueStore {<br /><br /> <<span style="color: #507874;">Row_Type</span>> SSTable<<span style="color: #507874;">Row_Type</span>> <span style="color: #ffc66d;">createTable</span>(String tableName<span style="color: #cc7832;">, </span>Class<<span style="color: #507874;">Row_Type</span>> type<span style="color: #cc7832;">,<br /></span><span style="color: #cc7832;"> </span>Map<String<span style="color: #cc7832;">, </span>Function<<span style="color: #507874;">Row_Type</span><span style="color: #cc7832;">, </span>Object>> schema<span style="color: #cc7832;">,<br /></span><span style="color: #cc7832;"> </span>Map<String<span style="color: #cc7832;">, </span>Function<<span style="color: #507874;">Row_Type</span><span style="color: #cc7832;">, </span>String>> indexes)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span><<span style="color: #507874;">Row_Type</span>> SSTable<<span style="color: #507874;">Row_Type</span>> <span style="color: #ffc66d;">createTable</span>(String tableName<span style="color: #cc7832;">, </span>Class<<span style="color: #507874;">Row_Type</span>> type<span style="color: #cc7832;">,<br /></span><span style="color: #cc7832;"> </span>Map<String<span style="color: #cc7832;">, </span>Function<<span style="color: #507874;">Row_Type</span><span style="color: #cc7832;">, </span>Object>> schema)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span><<span style="color: #507874;">Row_Type</span>> SSTable<<span style="color: #507874;">Row_Type</span>> <span style="color: #ffc66d;">createTable</span>(TableInfo<<span style="color: #507874;">Row_Type</span>> tableInfo)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span>List<String> <span style="color: #ffc66d;">desc</span>(String table)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> void </span><span style="color: #ffc66d;">close</span>()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span><<span style="color: #507874;">Row_Type</span>> SSTable<<span style="color: #507874;">Row_Type</span>> <span style="color: #ffc66d;">table</span>(String tableName)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> default void </span><span style="color: #ffc66d;">execute</span>(String sql<span style="color: #cc7832;">, </span>Consumer<RowValue> consumer) {<br /> <span style="color: #cc7832;">new </span>SqlAPI(<span style="color: #cc7832;">this</span>).execute(sql<span style="color: #cc7832;">, </span>consumer)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br />}</pre></div><div style="font-family: verdana;"> </div><div style="font-family: verdana;"><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;"><span style="color: #cc7832;">public interface </span>SSTable<<span style="color: #507874;">T_TYPE</span>> {<br /> List<String> <span style="color: #ffc66d;">cols</span>()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span><span style="color: grey;">//Search functions<br /></span><span style="color: grey;"> </span><span style="color: #cc7832;">void </span><span style="color: #ffc66d;">scan</span>(Consumer<<span style="color: #507874;">T_TYPE</span>> consumer<span style="color: #cc7832;">, int </span>limit)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> void </span><span style="color: #ffc66d;">search</span>(String indexName<span style="color: #cc7832;">, </span>String searchValue<span style="color: #cc7832;">, </span>Consumer<<span style="color: #507874;">T_TYPE</span>> consumer<span style="color: #cc7832;">, int </span>limit)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> void </span><span style="color: #ffc66d;">search</span>(String indexName<span style="color: #cc7832;">, </span>String searchValue<span style="color: #cc7832;">, </span>Collection<<span style="color: #507874;">T_TYPE</span>> container<span style="color: #cc7832;">, int </span>limit)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> void </span><span style="color: #ffc66d;">rangeSearch</span>(String index<span style="color: #cc7832;">, </span>String startKey<span style="color: #cc7832;">, </span>String endKey<span style="color: #cc7832;">, </span>Collection<<span style="color: #507874;">T_TYPE</span>> container<span style="color: #cc7832;">, int </span>limit)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span><span style="color: #507874;">T_TYPE </span><span style="color: #ffc66d;">get</span>(String pk)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> default </span>Map<String<span style="color: #cc7832;">, </span>Function<<span style="color: #507874;">T_TYPE</span><span style="color: #cc7832;">, </span>Object>> <span style="color: #ffc66d;">schema</span>() {<br /> <span style="color: #cc7832;">return null;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #cc7832;">default </span>Map<String<span style="color: #cc7832;">, </span>Function<<span style="color: #507874;">T_TYPE</span><span style="color: #cc7832;">, </span>String>> <span style="color: #ffc66d;">indexes</span>() {<br /> <span style="color: #cc7832;">return null;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #cc7832;">default </span>Object <span style="color: #ffc66d;">columnValue</span>(String col<span style="color: #cc7832;">, </span>Object row) {<br /> <span style="color: #cc7832;">return null;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: grey;">//Mutation functions<br /></span><span style="color: grey;"> </span><span style="color: #cc7832;">void </span><span style="color: #ffc66d;">insert</span>(<span style="color: #507874;">T_TYPE </span>row)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> void </span><span style="color: #ffc66d;">update</span>(<span style="color: #507874;">T_TYPE </span>record)<span style="color: #cc7832;">; </span><span style="color: grey;">// Secondary index needs rebuilding<br /></span>}<br /></pre></div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;">All the code is available on github @ <a href="https://github.com/ashkrit/corejava/tree/master/query" target="_blank">query</a> repo.</div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;">Each implementation is unit tested using contract based test, so feel free to experiment with it.</div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;">While implementation first i build KV related functionality and at the end added simple SQL interface. Adding simple SQL was done with help of Calcite SQL parser. Once basic query primitive was available ( point and range scan) then simple SQL interface was easy to add and that is the reason why i have "sql" as default function on interface. </div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;"><span style="font-size: large;">Conclusion</span></div><div style="font-family: verdana;">Ordered KV store are powerful data structure and can be used for solving many interesting problems. Many commercial and open source database are using some of techniques mention in post. </div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;">In this post i have intentionally not covered about how to handle different data types and how to optimized Index key. These are things that can be solved by better encoding that still maintains sort property of key.</div><div style="font-family: verdana;"><br /></div><div style="font-family: verdana;">I plan to cover about encoding rules in future post.</div></span></div>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com7tag:blogger.com,1999:blog-6543397255913469784.post-82924876614938490072020-11-07T23:18:00.000-08:002020-11-07T23:18:42.352-08:00Private method or state testing in JVM<p><span style="font-family: verdana;">Unit testing private method is not recommended but s</span><span style="font-family: verdana;">ome time we are in situation when unit test requires to inspect private state of object. As guideline we must avoid such type of design but some time especially when using some framework or library we are left with no options.</span></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipXCC0P3Tfj87TsoAwAbLsACoKodLGzZKQHxiGd_VIIjIQ5hMS7nmteDnn7Q1lFzk9aZ0QZAmDUO-h0elvKsAaUYXaPhQvDKi38nxxEX69vpsxft-QeviFQXuutK4uvzN8DPPjih2te2Zt/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="163" data-original-width="309" height="338" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipXCC0P3Tfj87TsoAwAbLsACoKodLGzZKQHxiGd_VIIjIQ5hMS7nmteDnn7Q1lFzk9aZ0QZAmDUO-h0elvKsAaUYXaPhQvDKi38nxxEX69vpsxft-QeviFQXuutK4uvzN8DPPjih2te2Zt/w640-h338/image.png" width="640" /></a></div><br /><br /><p></p><p><span style="font-family: verdana;">One of the such thing i found recently while writing some unit test around spark data frame. As part of one of the feature <a href="https://spark.apache.org/docs/2.3.0/api/java/org/apache/spark/sql/Dataset.html" target="_blank">dataframe/dataset</a> caching was required and no easy way to verify whether caching was done or not. I didn't want to add other layer of abstraction on Spark API to do this.</span></p><p><span style="font-family: verdana;">Code snippet that needs to be tested. </span></p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;"><span style="color: #9876aa; font-style: italic;">sparkSession</span>.read<br /> .parquet(<span style="color: #6a8759;">"...."</span>)<br /> .cache // Cache this DF/DS<br /> .createOrReplaceGlobalTempView(<span style="color: #6a8759;">"mysupertable"</span>)</pre><div><br /></div><div><span style="font-family: verdana;">Java has excellent support for </span><a href="https://en.wikipedia.org/wiki/Metaprogramming" style="font-family: verdana;" target="_blank">Metaprogramming</a><span style="font-family: verdana;"> from day 1 and reflection plays big role it that. Reflection can be used in such scenario.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><i style="font-weight: bold;">sparkSession.sharedState.cacheManager </i>maintain cached tables details. </span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">CacheManager is internal spark class and may change with new spark version , so test based on internal details has risk of breaking but it also gives good idea about internals of framework.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Lets try to access private state of cachemanager via java reflection.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Below code snippet will search the fields with specific pattern and make it accessible. </span></div><div><span style="font-family: verdana;"><br /></span></div><div><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace;"><span style="font-size: medium;"><span style="color: #cc7832;">def </span><span style="color: #ffc66d;">fieldValue</span>[<span style="color: #4e807d;">T</span>](fieldName: <span style="color: #4e807d;">String</span><span style="color: #cc7832;">, </span>obj: <span style="color: #cc7832;">Any, </span><span style="color: #72737a;">cls</span>: <span style="color: #4e807d;">Class</span>[<span style="color: #4e807d;">T</span>]): <span style="color: #4e807d;">T </span>= {<br /> <span style="color: #cc7832;">val </span>field = {<br /> <span style="color: #cc7832;">val </span>matchedField = <span style="font-style: italic;">classOf</span>[CacheManager].getDeclaredFields()<br /> .filter(_.getName().endsWith(fieldName))<br /> .map(f => {<br /> f.setAccessible(<span style="color: #cc7832;">true</span>)<br /> f<br /> })<br /> .head<br /> matchedField<br /> }<br /><br /> field<br /> .get(obj)<br /> .asInstanceOf[<span style="color: #4e807d;">T</span>]<br />}</span></pre></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">setAccessible(true) is the key thing, it is required for private or protected members.</span></div><div><span style="font-family: verdana;">One more to highlight that "endsWith" is used for matching rather than exact match because class under test is written in Scala and for Scala part of the field names are generated by compiler, so not possible to find exact match. Java reflection has getDeclaredField function that accept field name also. </span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Once member is marked as accessible then read/write is possible for that field.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Sample call to this API will look something like below</span></div><div><span style="font-family: verdana;"><br /></span></div><div><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace;"><span style="font-size: medium;"><span style="font-style: italic;">fieldValue</span>(<span style="color: #6a8759;">"cachedData"</span><span style="color: #cc7832;">, </span>sqlSession.sharedState().cacheManager()<span style="color: #cc7832;">, </span>LinkedList.<span style="color: #cc7832;">class</span>)</span></pre></div><div><span style="font-family: verdana;">Unit test code checking cache can be something like below </span></div><div><span style="font-family: verdana;"><br /></span></div><div><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace;"><span style="font-size: medium;">LinkedList cachedData = <span style="font-style: italic;">fieldValue</span>(<span style="color: #6a8759;">"cachedData"</span><span style="color: #cc7832;">, </span><span style="font-style: italic;">sparkSession</span>().sharedState().cacheManager()<span style="color: #cc7832;">, </span>LinkedList.<span style="color: #cc7832;">class</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;">int </span>before = cachedData.size()<span style="color: #cc7832;">;<br /></span><span style="font-style: italic;">loadVehicleTable</span>()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;">int </span>after = cachedData.size()<span style="color: #cc7832;">;<br /></span>Assert.<span style="font-style: italic;">isTrue</span>(after == before + <span style="color: #6897bb;">1</span>)<span style="color: #cc7832;">;</span></span></pre></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Reflection is very powerful and comes handy in such scenario but comes with tradeoff of relaxed type safety guarantee by treating method or variable as String but it is still better than not testing at all. </span></div><div><span style="font-family: verdana;"> </span></div><div><span style="font-family: verdana;">One more thing that i discovered during this exercise is that JVM spends some time in checking field access and it causes some overhead when reflection is used.</span></div><div><span style="font-family: verdana;">Turning off access check will make code fast also.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">With this trick now you can start testing some of the private function that you wished were public or left public only for testing.</span></div><div><span style="font-family: verdana;"><br /></span></div>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com1tag:blogger.com,1999:blog-6543397255913469784.post-16281857502868645882020-10-20T10:19:00.005-07:002020-10-20T10:19:58.212-07:00Histogram is not Instagram<p><span style="font-family: verdana;">Histogram is very popular technique to represent distribution of numerical data. This representation can be used for various thing like measuring latency of request, cardinality of data , grouping data in bins , frequency density of values.</span></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRx7HYZTXmj7FCbmuTjJ6G9Ji0GmcC04pNRl1TuQQ9RHFdP3rHb5DHBnCimqDgfAwzQ6FK-tzYsIALJF63ol9UV7bl0GPrwb3lEi-CcPdqPdQBYC8CFW11au2lR9R16vFN-OspwerQKB8h/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="302" data-original-width="512" height="378" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRx7HYZTXmj7FCbmuTjJ6G9Ji0GmcC04pNRl1TuQQ9RHFdP3rHb5DHBnCimqDgfAwzQ6FK-tzYsIALJF63ol9UV7bl0GPrwb3lEi-CcPdqPdQBYC8CFW11au2lR9R16vFN-OspwerQKB8h/w640-h378/image.png" width="640" /></a></span></div><span style="font-family: verdana;"><br /><br /></span></div><span style="font-family: verdana;"><br /></span></div><span style="font-family: verdana;"><br /><br /></span><p></p><p><span style="font-family: verdana;">In this post i will share some of the common use case with histogram.</span></p><div style="text-align: left;"><span style="font-family: verdana; font-size: large;">Measuring latency</span></div><div style="text-align: left;"><br /></div><div style="text-align: left;"><span style="font-family: verdana;">Assume that we want to measure response time of API call. First thing that comes to mind is stopwatch! Code snippet will look something like this. </span></div><div style="text-align: left;"><span style="font-family: verdana;"><br /></span></div><div style="text-align: left;"><span style="font-family: verdana;"><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;">StopWatch stopWatch = <span style="color: #cc7832;">new </span>StopWatch()<span style="color: #cc7832;">;<br /></span>stopWatch.start()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: grey;">//Some time consuming task<br /></span><span style="color: grey;"><br /></span>stopWatch.stop()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span>System.<span style="color: #9876aa; font-style: italic;">out</span>.println(stopWatch.toString())<span style="color: #cc7832;">;</span></pre></span></div><p><span style="font-family: verdana;">This is good starting point but does not take any further :-) For serious measurement we need more data points like best response time , worst , response time at some percentile like how much 90 % of request are taking. Such type of summary will allow us to understand service latency in more details also enable to take better decision. If data is available at different percentile then it can be plotted like below to understand it better.</span></p><p><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjL-xuD68vkRHbmG1CQdxEhLG2cxZn7GoElm3Sx0ML71skr14z_kWGdZYFZ1R9N_v_eV76GSy2T_v2JU-QnT00DyDD_8hEG_oTH3hWSFBHh1FLBtfK_6CPATmm7gd7LTqFwCDf5S47Uws-F/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="650" data-original-width="1020" height="275" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjL-xuD68vkRHbmG1CQdxEhLG2cxZn7GoElm3Sx0ML71skr14z_kWGdZYFZ1R9N_v_eV76GSy2T_v2JU-QnT00DyDD_8hEG_oTH3hWSFBHh1FLBtfK_6CPATmm7gd7LTqFwCDf5S47Uws-F/w558-h275/image.png" width="558" /></a></span></div><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><br /></span></div><p><span style="font-family: verdana;">For some consumer facing service, slow response means bad user experience and loss of revenue. Some of the industry like ecommerce, video streaming companies tracking response time proactively to retain customers.</span></p><span style="font-family: verdana;"><br /><span style="font-size: x-large;">Data cardinality</span></span><p></p><p><span style="font-family: verdana;">Tracking cardinality of some dimension is very important in data intensive application. It can be used for identifying skew in data or for finding optimal plan for execution of read or write request. Once of such examples is Index s</span><span style="font-family: verdana;">tatistics that is maintained by databases. Database cost based optimizer will use index stats to decided which index will result in faster execution. </span></p><p><span style="font-family: verdana;">For time series database tracking distribution of data based on day of time will be useful to find peek/busy time and it can used that information for having custom partition logic to manage load. Naïve way of doing this will be using frequency map but it will soon run into various performance issue and also limit options to do range query. </span></p><p><span style="font-family: verdana;">Histogram will come very handy for such scenario. Below charts shows distribution by time.</span></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiNFRFpfhKmt9zNxVKCQ1yxeY87lp_6qkzsmsYDHFyVqeW_f4NJStuENQcC_AX4jq9Jcozr9KSOXadF35D-J6cLNRIry5vtlazltZjCpAxRFuoJBwfBG5-gJVJmbJDYIJd0MeRYM64KcEo/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="228" data-original-width="477" height="236" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiNFRFpfhKmt9zNxVKCQ1yxeY87lp_6qkzsmsYDHFyVqeW_f4NJStuENQcC_AX4jq9Jcozr9KSOXadF35D-J6cLNRIry5vtlazltZjCpAxRFuoJBwfBG5-gJVJmbJDYIJd0MeRYM64KcEo/w493-h236/image.png" width="493" /></a></span></div><p><span style="font-family: verdana;"><span style="font-family: verdana;"><br /></span></span></p><span style="font-family: verdana; font-size: x-large;">Frequency density</span><p></p><p><span style="font-family: verdana;">This is popular use case in data science community. For example e-commerce company will be interested in know spend bins of customer or restaurant will be interested in knowing about frequency of visits. <br /></span></p><p><span style="font-family: verdana;">These metrics can help business in know about wealthy customers and come up with better strategy to retain or get new customers. Below example from Investopedia contains people density by age, very useful information for portfolio manager.</span></p><p><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-WHFNODmMWO-ZRmFWFTBjvX5kbnHWNVTVGUM8doRMCQ0LZ7oRvpDy38IG9brW93XYlPdDIBxITetFmCMQyAlTUqUNz1NW3W81yipi-7F5QkHobv7gbeICl-KLJzObRBydRwlofhSkbXdh/" style="margin-left: 1em; margin-right: 1em;"><img alt="" data-original-height="1297" data-original-width="2048" height="304" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-WHFNODmMWO-ZRmFWFTBjvX5kbnHWNVTVGUM8doRMCQ0LZ7oRvpDy38IG9brW93XYlPdDIBxITetFmCMQyAlTUqUNz1NW3W81yipi-7F5QkHobv7gbeICl-KLJzObRBydRwlofhSkbXdh/w480-h304/image.png" width="480" /></a></span></div><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><br /></span></div><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><br /></span></div><span style="font-family: verdana;">Histogram has many more interesting use case. One of the cool property about histogram is that it is <a href="https://en.wikipedia.org/wiki/Commutative_property" target="_blank">Commutative</a>. Items order makes no different in final output due to which it is possible to build histograms in parallel and merge it later.</span><p></p><p><span style="font-family: verdana;">Another important property of histogram is <b style="font-style: italic;">cumulative, </b>this property allows to do cumulative calculation by merging multiple bins/buckets.</span></p><p><br /></p><h2 style="text-align: left;">Code Snippet </h2><p><span style="font-family: verdana;">Now lets look at one of the histogram library that can be used for various use case.</span></p><p><span style="font-family: verdana;"><a href="http://hdrhistogram.github.io/HdrHistogram/" target="_blank">HdrHistogram</a> by Gil Tene is battel tested library build for measuring latency of application. I found this library to be useful for other use case also. This library is very memory efficient and designed to work in low latency environment and also has very compact loss less serialization.</span></p><h3 style="text-align: left;"><span style="font-family: verdana;">Latency Measurement</span></h3><div><span style="font-family: verdana;"><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;">Histogram histogram = <span style="color: #cc7832;">new </span>Histogram(<span style="color: #6897bb;">2</span>)<span style="color: #cc7832;">;<br /></span><span style="font-style: italic;">range</span>(<span style="color: #6897bb;">0</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">1000_000</span>)<br /> .forEach(v -> {<br /> <span style="color: #cc7832;">long </span>timeTaken = <span style="font-style: italic;">doSomething</span>(v)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: #b389c5;">histogram</span>.recordValue(timeTaken)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>})<span style="color: #cc7832;">;<br /></span>histogram.outputPercentileDistribution(System.<span style="color: #9876aa; font-style: italic;">out</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">1.0</span>)<span style="color: #cc7832;">;</span></pre></span></div><div><span style="font-family: verdana;">Histogram class is part of HdrHistogram lib, it accepts various parameter. In this example we are specifying precision to use. This value can range in 0 to 5, precision has direct impact on size of histogram.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Another important thing about this library is that histogram will auto resize based on input value and this property plays big role in avoiding upfront allocation.</span></div><div><span style="font-family: verdana;">Running above code will produce output like below </span></div><div><span style="font-family: verdana;"><br /></span></div>
<script src="https://gist.github.com/ashkrit/84686c08876b4f72dd8d6eb715d64953.js"></script>
<div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Lets try to understand output. Output contains 4 columns ( Value , Percentile, Total Count, 1/1-Percentile)</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><b><i>Value </i></b>- Input value for given percentile </span></div><div><span style="font-family: verdana;"><b><i>Percentile </i></b>- % of records in this group/bucket.</span></div><div><span style="font-family: verdana;"><b>Total Count</b> - Count of records in bucket.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Lets pick some bucket values to understand how to use it.</span></div><div><span style="font-family: verdana;"><br /></span></div>
<script src="https://gist.github.com/ashkrit/1b80f2f71af19ab6ad0c07ad06cc2d7f.js"></script>
<div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><i><b>50% request took 5 Seconds</b></i> - This metrics is very common when measuring but of no use because it is like average/mean. If you want to do some serious measurement then stay away from average.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><i style="font-weight: bold;">97.5% request took 9.7 seconds - </i>This is where it becomes interesting, 25% of request is taking double! It is really bad user experience. </span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><b style="font-style: italic;">99.86% request took 10+ seconds - </b>This provides real worst case, around 200K+ request is part of this group and it is takes double time as compared to average. </span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Having latency broken down by percentile provide great insight into what is real customer experience. If you are e-commerce shop then every micro second counts and can translate into big gain or loss. </span></div><div><span style="font-family: verdana;">Trading application also sensitive to latency spike and makes big difference on how fast buy/sell trades can be matched.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Another thing that is very common in micro services world is that to render full page 100s of API call is required and it adds risk that atleast one of the API call to get hit by worst case latency, so overall user experience become bad:-(</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Next time anyone talks about latency then ask for 99.9999 percentile to know about real impact.</span></div><div><span style="font-family: verdana;"> </span></div><h2 style="text-align: left;"><span style="font-family: verdana;"><span style="font-weight: normal;">Data cardinality</span></span></h2><div><span style="font-family: verdana;"><span style="font-weight: normal;">Database has optimizers like rule based and cost based. Cost based optimizer generate plan based on cost of operations like Join, filter etc. and select best plan quickly. </span></span></div><div><span style="font-family: verdana;"><span style="font-weight: normal;">One of the important factor in deciding which plan to use is based on data distribution/stats. Each index maintains data cardinality stats so that optimizer can quickly figure out which index to use. </span></span></div><div><span style="font-family: verdana;"><span style="font-weight: normal;"><br /></span></span></div><div><span style="font-family: verdana;">Cardinality is also useful to find data skew and to come up with plan to handle data skew. Distributed computing framework like Apache Spark suffer from skew and it depends on engineer to add some intelligence in code to avoid Spark job failure.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Trust me real data is always Skewed. </span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Lets take a scenario that an E-commerce Giant want to keep some statistics like purchase volume at merchant level, so that it can come up better platform scaling options.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Histogram internally maintains frequency count, so it can be used to calculate total volume at merchant level in efficient way.</span></div><div><span style="font-family: verdana;">For this scenario lets assign unique integer id to each merchant and record purchase in histogram, for recoding purchase we can pass merchant id in histogram.</span></div><div><br /></div><div>Some code on how this can be done. </div><div><br /></div><div><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;">Histogram histogram = <span style="color: #cc7832;">new </span>Histogram(<span style="color: #6897bb;">2</span>)<span style="color: #cc7832;">;<br /></span><span style="font-style: italic;">range</span>(<span style="color: #6897bb;">0</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">1000_000 </span>* <span style="color: #6897bb;">100</span>)<br /> .forEach(v -> {<br /> <span style="color: #cc7832;">int </span>merchant = <span style="font-style: italic;">merchant</span>()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: #b389c5;">histogram</span>.recordValue(merchant)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>})<span style="color: #cc7832;">;<br /></span>histogram.outputPercentileDistribution(System.<span style="color: #9876aa; font-style: italic;">out</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">1.0</span>)<span style="color: #cc7832;">;</span></pre></div><div><span style="font-family: verdana;">In above example merchant id from 100 Million transaction are used to build histogram. To show some real skew i have added 50% weight to one particular merchant (i.e merchant id 101).</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Running above code will produce something like below</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><div><b> Value Percentile TotalCount 1/(1-Percentile)</b></div><div><br /></div><div> <i> 1.00 0.000000000000 1043 1.00</i></div><div><i> 101.00 0.100000000000 50050299 1.11</i></div><div><i> 101.00 0.200000000000 50050299 1.25</i></div><div><i> 101.00 0.300000000000 50050299 1.43</i></div><div><i> 101.00 0.400000000000 50050299 1.67</i></div><div><i> 101.00 0.500000000000 50050299 2.00</i></div><div> 10047.00 0.550000000000 55023464 2.22</div><div> 20095.00 0.600000000000 60047899 2.50</div><div> 30079.00 0.650000000000 65038139 2.86</div><div> 40191.00 0.700000000000 70092127 3.33</div><div> 50175.00 0.750000000000 75086627 4.00</div><div> 55039.00 0.775000000000 77520056 4.44</div></span></div><div><span style="font-family: verdana;"> </span></div><div><span style="font-family: verdana;">50% of transactions (i.e 50 Million) are part of 101 merchant. This information can be used for optimization of read query or write query or coming up with some good product offerings.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">One thing to remember that histograms can be merged, so it is possible to calculate histogram using multiple nodes and later merge it to get full view, infact it is possible to build histogram incrementally. </span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Next time if data skew is troubling you then try Histogram.</span></div><div><span style="font-family: verdana;"><br /></span></div><h2 style="text-align: left;"><span style="font-family: verdana; font-weight: normal;">Frequency Density </span></h2><div><span style="font-family: verdana; font-weight: normal;">This use case takes histogram to next level. Lets extend E-commerce example and now we want to rank customers based on total dollar value they spend.</span></div><div><span style="font-family: verdana; font-weight: normal;"><br /></span></div><div><span style="font-family: verdana; font-weight: normal;">Histogram comes handy, we have to just feed customer wise total spend and get the buckets that can used for ranking customer. </span></div><div><span style="font-family: verdana; font-weight: normal;"><br /></span></div><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: 'JetBrains Mono',monospace; font-size: 9.0pt;">Histogram histogram = <span style="color: #cc7832;">new </span>Histogram(<span style="color: #6897bb;">2</span>)<span style="color: #cc7832;">;<br /></span><span style="font-style: italic;">range</span>(<span style="color: #6897bb;">0</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">1000_000 </span>* <span style="color: #6897bb;">100</span>)<br /> .forEach(v -> {<br /> <span style="color: #cc7832;">long </span>spend = <span style="font-style: italic;">spendValue</span>()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: #b389c5;">histogram</span>.recordValue(spend)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>})<span style="color: #cc7832;">;<br /></span>histogram.outputPercentileDistribution(System.<span style="color: #9876aa; font-style: italic;">out</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">1.0</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span></pre><div><span style="font-family: verdana; font-weight: normal;"><br /></span></div><div><span style="font-family: verdana; font-weight: normal;">Again taking 100 million purchase and this time adding spend value, this will create buckets based on spend and later customers can be ranked using these buckets.</span></div><div><span style="font-family: verdana; font-weight: normal;"><br /></span></div><div><span style="font-family: verdana; font-weight: normal;">Buckets output will look something like below.</span></div><div><span style="font-family: verdana; font-weight: normal;"><br /></span></div><div><span style="font-family: verdana;"><div> Value Percentile TotalCount 1/(1-Percentile)</div><div><br /></div><div> 0.00 0.000000000000 4963 1.00</div><div> 2007.00 0.100000000000 10036946 1.11</div><div> 4015.00 0.200000000000 20077808 1.25</div><div> 6015.00 0.300000000000 30075647 1.43</div><div> 8031.00 0.400000000000 40157837 1.67</div><div> 10047.00 0.500000000000 50238571 2.00</div><div> 11007.00 0.550000000000 55038969 2.22</div><div> 12031.00 0.600000000000 60159541 2.50</div><div> 13055.00 0.650000000000 65279759 2.86</div><div> 14015.00 0.700000000000 70077894 3.33</div></span></div><div><span style="font-family: verdana; font-weight: normal;"> </span></div><div><span style="font-family: verdana;">This output can be interpreted like 10 million customer spends in range of 0 to 2K, another 10 million spends 2K to 4K and so forth. Higher bucket will contain premium customers. </span></div><div><br /></div><div><span style="font-family: verdana;">Histogram has many more application, it can be used in machine learning to build features because they allow various vector based arithmetic like add/substract. </span></div><div><br /></div><div><span style="font-family: verdana;">All the code used in this post is available @ <a href="https://github.com/ashkrit/corejava/tree/master/playground/src/main/java/measure" target="_blank">measure</a> github</span></div><div><span style="font-family: verdana;"><br /></span></div>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com0tag:blogger.com,1999:blog-6543397255913469784.post-9738176654707871902020-09-27T09:07:00.001-07:002020-09-27T09:17:15.762-07:00Let the Stream flow<p><span style="font-family: verdana;">Java Streams is one of the powerful feature of JDK that is based on Lambda.</span></p><p><span style="font-family: verdana;">This post is quick refresher of Streams concepts using <i><b>learning test</b></i>.</span></p><p><span style="font-family: verdana;"></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: verdana;"><a href="#" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="145" data-original-width="347" height="207" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXVD4lAPOhXLilndrWfteqEm9aeZCGc_OCcxZq4xnhhnE2QyGH9crXDdoJWR_jQyzgzqDM58y-NtpfUGZxD6BkqzULgShtAYWJ95UMk8I6AZ_mtIeg7GzGyoSLJxstzChAJDAohrs_esoq/w578-h207/image.png" width="578" /></a></span></div><span style="font-family: verdana;"><br /><br /></span><p></p><p><span style="font-family: verdana;">Streams are made up of </span></p><p><span style="font-family: verdana;">Source |> map | filter |>reduce </span></p><h2 style="text-align: left;"><span style="font-family: verdana;">Stream basic</span></h2><p><span style="font-family: verdana;">Stream is computation pipeline that start with Source and series of intermediate operation and ends with terminal operation.</span></p><p><span style="font-family: verdana;">Stream is expressed as pipeline of functional transformation and it enable optimal execution strategy like lazy execution, short circuiting & fusion of operations.</span></p><p><span style="font-family: verdana;">These execution strategy allows to avoid un-necessary materialization of data because many things are done as Single pass or by multiplexing.</span></p><p><span style="font-family: verdana;">Streams can be also seen as <a href="https://en.wikipedia.org/wiki/SIMD" target="_blank">SIMD</a> at application layer. Stream is made of state less & state full operations, state less operations are part of single stage of pipeline. </span></p><p><span style="font-family: verdana;">State less operation like ( map, flatmap,filter) are fused to provide optimal execution and only state full operation like (sort, takewhile , drop while,limit, distinct ) can add barrier or new stage in pipeline.</span></p><p><span style="font-family: verdana;">Since stream is computation pipeline, so it takes advantage of CPU caches by performing all the transformation on single element while it is hot in cache. </span><span style="font-family: verdana;">Some time this execution strategy is also called depth first, goes to leaf and process it.</span></p><p><span style="font-family: verdana;"><span style="font-family: verdana;">Accessing data while it is hot in CPU cache makes big difference in performance and you can read about it more on post <a href="http://ashkrit.blogspot.com/2013/06/cpu-cache-access-pattern.html" target="_blank">cpu-cache-access-pattern</a></span> </span></p><p><span style="font-family: verdana;"></span></p><p><span style="font-family: verdana;">Stream ends with terminal operations. </span><span style="font-family: verdana;">Terminal operation are short-circuit ( allMatch, findFirst,anyMatch) or non short circuit like (reduce , collect , forEach)</span></p><p><span style="font-family: verdana;">Short circuit will cause early termination and very useful for search related operation.</span></p><p><span style="font-family: verdana;">Non short circuit operation will touch every element of stream, reduce & collect is example of such operation and allows to solve very complex problems.</span></p><p><span style="font-family: verdana;">Streams favors reduction/folding over imperative accumulation, reduction are easy to make it parallel and simple to understand. It also opens up embarrassing parallel opportunity. </span></p><p><span style="font-family: verdana;">Reduction also has property of associativity for example</span></p><p><span style="font-family: verdana;">(+(+ (+ a b) c ) d) = (+ (+ a b) (+ c d))</span></p><p><span style="font-family: verdana;">Above example is reduction using <i>Plus(+) </i>operator, left is sequential reduction and right one is parallel but output is same for both of the execution path.</span></p><p><span style="font-family: verdana;">Associative operator are embarrassingly parallel. </span></p><p><span style="font-family: verdana;">Power of stream is in advance reduction patterns and collectors class has tons of example.</span></p><p><span style="font-family: verdana;">Collector accepts supplier, accumulator & combiner. These 3 things are composed to do very complex reduction.</span></p><p><span style="font-family: verdana;">Lets look at String based reduction by looking at world famous String joiner. </span></p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace;">values<br /> .stream()<br /> .collect(StringBuilder::<span style="color: #cc7832;">new, </span>(sb<span style="color: #cc7832;">, </span>value) -> sb.append(value)<span style="color: #cc7832;">, </span>(sb1<span style="color: #cc7832;">, </span>sb2) -> sb1.append(sb2))</pre><p><br /></p><p><span style="font-family: verdana;">If stream ends with non short circuit operation then records are processed in batch(forEachRemaning) and in case of shortCircut it is process as single at a time(tryAdvance)</span></p><p><span style="font-family: verdana;"><br /></span></p><h2 style="text-align: left;"><span style="font-family: verdana;">Stream Operations Flag</span></h2><div><span style="font-family: verdana;">Every stream has some characteristic that is used by stream framework for optimization.</span></div><div><span style="font-family: verdana;">As a application programmer we don't get exposed to stream operations flag but knowing these will help in understanding optimization technique used by stream.</span></div><div><span style="font-family: verdana;"> </span></div><div><span style="font-family: verdana;">Stream at source is defined with </span><span style="font-family: verdana;">characteristic</span><span style="font-family: verdana;"> and stream operation<i>(map, filter, limit, sort etc</i>) may preserve, clear or inject new </span><span style="font-family: verdana;">characteristic.</span></div><div><span style="font-family: verdana;">Terminal operation will result in inspecting all the </span><span style="font-family: verdana;">characteristic</span><span style="font-family: verdana;"> and select optimized code path for execution.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Very simple stream </span><span style="font-family: verdana;">characteristic</span><span style="font-family: verdana;"> is Parallel, this is taken in account by stream framework to use single thread vs multiple threads for execution.</span></div><div><span style="font-family: verdana;">Some of the other Stream flags are</span></div><div><ul style="text-align: left;"><li><span style="font-family: verdana;">Distinct - Stream has distinct values.</span></li><li><span style="font-family: verdana;">Sorted - Element are sorted by natural order</span></li><li><span style="font-family: verdana;">Ordered - Element has order </span></li><li><span style="font-family: verdana;">Size - Finite size, important for splitting .</span></li><li><span style="font-family: verdana;">Short Circuit - stream can be short circuit, it may be due to find, limit etc</span></li></ul><div><span style="font-family: verdana;">Lets take distinct stream operation to understand how it gets optimized.</span></div></div><div><span style="font-family: verdana;">Once we have distinct element then we can count the number of element using below code snippet. </span></div><div><span style="font-family: verdana;"><br /></span></div><div><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace;"><span style="font-size: medium;">values<br /> .stream()<br /> .distinct()<br /> .count()</span></pre></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Distinct count of element can be implemented in many ways based on underlying collection of stream.</span></div><div><h4><ul style="text-align: left;"><li><span style="font-family: verdana;">List/LinkedList </span></li></ul></h4><span style="font-family: verdana;"> If underlying collection is list then only brute force way can be used for distinct count. We have to allocate Set and keeping adding element in the set and then return size. This will cause some memory pressure on system when collection is large.<br /></span><span style="font-family: verdana;"> </span><br /><h4><ul style="text-align: left;"><li><span style="font-family: verdana;">SortedSet</span></li></ul><span style="font-family: verdana;"><span style="font-weight: 400;">If underlying collection is Sorted collection like </span><i>Tree Set</i><span style="font-weight: 400;"> then distinct count does not need any memory allocation and distinct can be computed by using simple loop checking current and previous value, code snippet doing distinct count </span></span></h4><div><span style="font-family: verdana;"><span style="font-weight: 400;"><br /></span></span></div><div><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "JetBrains Mono", monospace; font-size: 9pt;"><span style="color: #cc7832;">static int </span><span style="color: #ffc66d;">distinct</span>(SortedSet<String> values) {<br /> Iterator<String> itr = values.iterator()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> if </span>(!itr.hasNext()) <span style="color: #cc7832;">return </span><span style="color: #6897bb;">0</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span>String previous = itr.next()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> int </span>itemCount = <span style="color: #6897bb;">1</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> while </span>(itr.hasNext()) {<br /> String next = itr.next()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> if </span>(!previous.equals(next)) {<br /> itemCount++<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>previous = next<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> }<br /> <span style="color: #cc7832;">return </span>itemCount<span style="color: #cc7832;">;<br /></span>}</pre></div><h3><ul style="text-align: left;"><li><span style="font-family: verdana;">Set</span></li></ul><div style="text-align: left;"><span style="font-family: verdana; font-size: small; font-weight: normal;">If underlying collection is Set then it is just calling size function on it!</span></div><div style="text-align: left;"><span style="font-family: verdana; font-size: small; font-weight: normal;"><br /></span></div><div style="text-align: left;"><span style="font-family: verdana; font-size: small;"><span style="font-weight: normal;">This is simple example on how Stream pipeline can take advantage of Stream Ops to plugin optimal code. This seems like the way database </span><span style="font-weight: 400;">optimizer works and shows power of declarative programming.</span></span></div><div style="text-align: left;"><span style="font-family: verdana; font-size: small;"><span style="font-weight: normal;"> </span></span></div><div style="text-align: left;"><span style="font-family: verdana; font-size: small;"><span style="font-weight: normal;"> </span><span style="font-weight: normal;">We can take this further by adding new Stream ops like </span><i>approx</i><i> distinct</i><span style="font-weight: normal;"> and it can be based on <a href="https://en.wikipedia.org/wiki/HyperLogLog" target="_blank">HyperLogLog</a> p</span><span style="font-weight: 400;">robabilistic data structure and it can handle any types of collection with very less memory overhead and by trading off little bit of accuracy. </span><span style="font-weight: 400;">I shared about some of the </span><span><span style="font-weight: normal;">p</span></span><span style="font-weight: 400;">robabilistic data structure in <a href="http://ashkrit.blogspot.com/2017/10/data-structure-for-big-data.html" target="_blank">data-structure-for-big-data</a> post.</span></span></div><div style="text-align: left;"><span style="font-weight: 400;"><span style="font-family: verdana; font-size: small;"><br /></span></span></div><div style="text-align: left;"><span style="font-weight: 400;"><span style="font-family: verdana; font-size: small;">Other flags also does lots of magic to make code fast.</span></span></div><div style="text-align: left;"><span style="font-size: small;"><span style="font-family: verdana; font-weight: 400;"><br /></span></span></div></h3><h2 style="text-align: left;">Conclusion</h2><div><span style="font-weight: normal;"><span style="font-family: verdana; font-size: small;">Stream is very powerful abstraction for solving problem using declarative way.</span></span></div><div><span style="font-weight: normal;"><span style="font-family: verdana; font-size: small;">Enjoy the various examples of streams in github project <a href="https://github.com/ashkrit/corejava/tree/master/streams" target="_blank">streams</a>. Examples are organized in chapters and it cover simple to advance usage patterns.</span></span></div><div><br /></div></div><div><span style="font-weight: normal;"><span style="font-family: verdana; font-size: small;"><br /></span></span></div>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com0tag:blogger.com,1999:blog-6543397255913469784.post-82837996060779350452020-08-11T04:18:00.007-07:002020-08-11T04:38:41.290-07:00Speak the language of domain - part 2<p><span style="font-family: verdana;">While researching for post - <a href="http://ashkrit.blogspot.com/2020/08/speak-language-of-domain.html" id="gmail-http://ashkrit.blogspot.com/2020/08/speak-language-of-domain.html" target="_blank">speak-language-of-domain</a> i spent some time building external DSL and experimented with parser and compiler.</span></p><p><span style="font-family: verdana;">Parser & compiler are important components for external DSL because it converts external DSL to executable.</span></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;">We will take a few examples of external DSL and look at what is required to build a full solution.</span></p><h3 style="text-align: left;"><span style="font-family: verdana;">Rule Engine</span></h3><div><span style="font-family: verdana;">I will use below DSL as external dsl and try to convert it into an executable.</span> </div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;"><br /></span></div>
<script src="https://gist.github.com/ashkrit/5213fc7898eb15f72f69cef263558788.js"></script><div><div><span style="font-family: verdana;">We allow users to upload dsl files and change the behavior of the system at runtime based on rules in the dsl file. Such a type of feature will make the system very flexible and powerful.</span></div><div><span style="font-family: verdana;">Remember Peter Parker principal <i>"With great power comes great responsibility" , </i>so use such things in the production system at your risk!</span></div></div><div><span style="font-family: verdana;"> </span></div><p><span style="font-family: verdana;"><br /></span></p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><img alt="" height="289" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAACJwAAANxCAYAAACrSTHJAAAgAElEQVR4AezdCXBUZ5ruecy+I7PvyNgGm1UYjMHYILNvRhtakZCQ2L2A9xUQqxYMkg3Y2K5b5t653dXVXd327dpX7Fq6uqvcRVW79g2TmWImJuIGs9yJmImJiXfi/b7zpc45eYQRCAOpPxEn3nNOJoL8SUiOyMfP20X4hQACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIBAOwS6tOO5PBUBBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAASFwwhcBAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAQLsECJy0i4snI4AAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCBA44WsAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAoF0CBE7axcWTEUAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBAic8DWAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggg0C4BAift4uLJCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgRO+BpAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQaJcAgZN2cfFkBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQInfA0ggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIItEuAwEm7uHgyAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAIETvgYQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEE2iVA4KRdXDwZAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAgMAJXwMIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAAC7RIgcNIuLp6MAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggg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SIAESIAESIAESIAESIAESIAESIAETAI0nFg08vLy1CZvsxZtkHf4QxSduqTqdVx6slrPK61GkajHsd6aW3+iGnLu0hPVWCfzpj6m59ZZutbUx6ohY30EtB7XWuihhcerIfOmFhzTY5dKkonMh6iYSuxkE1uV2cRMNrHPmXDChJNSPzKYcGLV6FxUFTvJe926Yo8ee6lZozP3zpfQpssAx2wiqSas0DH/XPGcBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEjgViJAw4n1aVy+fBmywSuJAkOmpimzCRNOguYTJpz4YSabSJWOjJlw4oOq1Sn1IaNG84nfqtfRml5iqKrX8etanRKtaQdr0eus1zGTTST9xEk2sc/NZBM76eQmJJyMWPCAYzSRNUhqvi5cuHAr/a3gayEBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABFwEaTgwcxcXFzqZv4rYfMeHEVaMTgJhOXAcTTphwUqPJxGeZTLSmlxiqTCY+y2Ri6UGtaV56nSYTVatjGUrqZjK5qCt19nmrJJhItY6pUrETmmxij72STZL3+iDzC7e+iU79JjrrDlNNjAWZpyRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAArc0ARpOjI9HUk6ioqLU5m9EZDSYcMKEk5wjVrKJrVayCRNOgskmTDgRs4nPqtUxVOZCDtN8ErN0P5q1bO+YTWJiYnDu3DljReIpCZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACdy6BGg4CflsTp8+7WwAT1i6DUWnLgWTTk5W67HSahSJGsd669zRE9WQ8/WmnqjGOhmb+pieW2fpWlMfq4aM9RHQelxroYcWHq+GzJtacEyPXXqsGmocogUytpJNbFV1OqHpJjJmwgkTTphwopNNriLhJP6uX6FT3wnOOiMVOpKuxH8kQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkQAIkcDsRoOHE49OSpAHZBG7arCWyS952m05OmaaTS9pwIqYUZTYJqjadXFKGk6KTlyzTia0ephPLgGKbTmw1zSdB44ltQBFziT43VZtN9G2h5y7TyfGrNJ0cq1aVOgVVWvNNVeaTaqzxUJkLPVY/oudMlfMaj6PWbZbmiR4NQNR1VOpxrqN+dXtupR8yF6qSYCLzpuYc0WNPZcIJJNEks5QJJ7pWx2fV6xjJJsp8Ep5usvyhTzF45gbc0aSpYzZhqonHAswpEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiCB24IADSceH5PUWojhRI6+I+cw4UQST5hwotJMxJiSLaaTCrfKXPZhPWfrKmss6nWsPKTnvVTmgodPn5d7a5bMl/thapaMy3wwNdMaKy3zQzTTVDGRyNhLazCZqDqdEh8yjKSTdBmX+GCqnDvHQX2eZupBH2TseRyw5k2Vc+tIDdX9Pshcqqn7fUiRsaVy7hz7rHNHLyoTSco+b03eq+dNTb5CwsmUrCfQqmNvZ12JiIhAZWWlx+rDKRIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARK4PQjQcFLD5yQVF7bpZO6aY0HTCRNOwISTgGU+CVjmE7dmVwQc84lpOhFTipfxROa8TCf2fNB4YphQyq3zEBWDiTafBFWbTsSQIiaTUA0xndTFhFKD+cQ0nYjhRMbadOLW9BI9VqpMJ35lNJGxNpzUoqbpRAwqhvFEzq9kPjFNJ3Uzn+gEE0kzMQ8xmNQl4SR+4y/Rud9EZy2RNSUvLw+XL1+uYeXhNAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAncHgRoOKnlc4qKilIbxS3adETe4Y/c9TliPFE1OkHVNTrVVo1OtVWjE6JWdc56Ux/TFTteNToyF6zSCejz41oLPVRX6ARU1Y7cLuO61OgUHtP3KxCVRBND85lwoip3JNWECSd+ZITW6TDhBKEJJ4u3v42+Y1NcRhOpzzl79mwtKw5vIgESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIIHbhwANJ7V8VrI5bKecRMUsRNGpS8GkEzGbyNjUk9b45CXLdGLoCTGiXHJMKOtkbJpOrHPbdGKrmE3kvkHTiftcDCVym6nadKLnQs/rYj4xzSa2+USZTo5VQ5tPtOZXGfpoADJe46EyF3qsfkTPmSrnNR5HrdsszRM9GoCo66jU41xH/er23Eo/ZC5UxUAi86bmHNFjT1WmEyacqNodqdExzCdMOPFh6c6PMWzuPWjSvJWzdrA+p5ZFljeRAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAmQAAnctgRoOLnCR7dlyxZn41hX69gmk2CyiZ10woQTMZ2Em0tss4lpLpG5Gs0ltvHEMZn41X3zjvotk0lQldmkMmgqkbGYSmy9fpOJ36rPCapU40jaialynn1Yz9lq1ul4VenUvUbHpyp3VpZ7a5bMl0tVTlB1jY5P1ehklWnNNLVM6nVC6nTEPCLzXlpDjU6mmE6YcIJlD2qjSbNWEc56wfqcKyyuvJkESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESOC2JkDDSR0+PqnCkM3jZi3bIHP36+HJJnbSCRNOmHCijCgBx3ximk7ElOJlPJG5uptP9H3FYCI/E6piNNHmk6Bq84kYUsRkEqohppO6mFBqMJ80xoSThPvex5BZdyLUaBIXF8f6nDqsrbwLCZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZDA7UuAhpM6fHYXLlyA1GKI6SQiciDyj36mq3WkTsc4mHDChBMz6cQ0m3gZTepuMvFONrETT8xkEzvphAknPqTu9yH1gKX7fUiRsaVy7hz7rHNHLyJFzvd5a/Lei1i49beImrDSlWYi60NUVBTOnDlTh1WFdyEBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiCB25sADSd1/PzOnj3rbC73HDIN6x7zhSedNOKEE6nIya+qZsIJE05gJp2kS91OiR+mpltjpQd9EE0zNO2gHnvqAZ+6b5qpcm4dYjCRc0dN08kBt+mkbuYTMZ4Ej3mbXkHv0cvxjTuaOOuBGE0kBen06dN1XE14NxIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARIgARK4/QnQcHIVn2FxcbGzyTxkWror3USSTphwwoQTJpzY5hIxkojZRKucO4cyl2jjiMwps8lBy0jipaa5RG43DCYuc4ltNgkxmaikk+tMOJm/+VX0GrnE+f0Xk4kcrM65igWUdyUBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEmhQBGg4ucqPMy8vz9l0Hj23UJtOTl2yzCdB1eaTS5YJ5RLWnxBDiq3VWCfjE26VuXWP6Tlb11pjUa+j8LieN1XOazoKjgXUbS49Vg01trRAtCoAU/OtsdKqaohKoomtTDjxI6ciAGU4ET3sV+dmrY7c5lWtI3N1r9fR95XqHPmZUJUqHV2rE1Rdr+OHaGZZqPrUXGZZDVpqzZta6kemjEv9yDDUTDYRo4mMtenErbdLwsmyne8jZuk+dOo70fmdt40msg6cO3fuKlcP3p0ESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAEGg4BGk6u4bN0mU7i1zpJJ0w4YcIJE05sc8ntm3AyMfUoIofMDTOZREREQFKOLly4cA2rBn+EBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABBoWARpOrvHzjImJcTakh8/KZsLJozrxxCvpROZCj9WP6DlT5bzG46h1m6V5okcDEHUdlXqc66hf3Z5b6YfMhWrOET1vas6RAGTsqRXWvKViMLGTTWzNbgQJJ2ayiZ10cjsnnCy4+ywGzViP5m06O7/XdpqJ1OacPn36GlcK/lhjJ/DPf/4Tf//73/HXv/4Vfr8f58+fxyeffIKPPvoI7777Lt566y38+te/xuuvv44333wT7733Hj7++GN8/vnnuHjxovq5y5cv46uvvmrsKPn+SYAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAEbjECNJxc4wciG4Cm6aTPiNlYc/QzZTxxkk5UjY6uzpE5VasTUqOjanVqqdGRap1glU5Anx/XWuihukpH1+bI7TJ21efY45AancIr1emoCh2dYKJrdALIVyYTbRyROW02CTeX2GYT01wiczWaS2zjiWMy8av75h31WyaToCqzSWXQVCJjMZXYev0mEzGUiMkkqNpkoitz5DaVaiJq1ejYatbpeFXp1L1Gx2fV53hrVrnPqtEJqq7R8akanawyrVKbo2t1fMgqk3qdkBodszZHbjfHNdToKLOJVZ/jrtG59RNOkoo/w4TkQ+gSNSXMZCJpJlu2bGGayTWujw31x3w+H1599VU888wzOH78OPbu3auuk+zsbCxevBjTpk1DdHQ0OnXqFHZN2Qam+tBWrVqhQ4cO6Nq1K3r16oX+/ftjyJAhGDVqFMaPH4+pU6di9uzZSEpKQn5+Pu69916UlJTg1KlTeO6559R7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68PjRu36Y+oWj9V0MnVLLpykE9kXCeXM8okxZmdTXurxOrnGWB3f9CBlox61Y0859xqrs9GjRujI3uQCDhmdI7fZU4/X0XvmeVHH6yjpJNuUT3QmrctLLZ14lGQi+1o2MbP45BMRTURQUamkE32uBRSRTPQ6LwsvnRQsn+QY8kle2ptNzKYTNpx4N5uYEkpZbTjpM+FeVKhSy3q/GDx4ML744otCv9fwjiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAheGAIWTC8Od35UESIAESgWBH3/8EQ8//DCGDRtmtZrICIu9e/eq/V69eqnbHnjgAXz77bdndc2nTp3CiBEjrA+RW3QfZUkmTrLJmSUTD1I3eR9aMtEiidw2RaQSW9rlErlNCSY2ycRLNnEUTHIMuSR/arEkR4knk9brLKpkIq0myZZkYms2MVtOHJpNREzxbTYx1/ZmE9nL12xitp1YzSZm0wkbTtRoHZ+mEzac6BE7Xs0mMmZHmk7OoeHkuptfQ4P2w633iOrVq+Oee+45q/cZ3pkESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESODCEaBwcuHY8zuTAAmQwAUlICN00tLS0LVrVyWbtG/fHrNnz8aTTz6J6dOno3PnzpgxYwbeeeedIl/n77//jqCgvDEZbQOiVdMJG048UHJKtk6RTbykE0s+yWs20eN1vJtN2HDixoRiH6/jNsbrOKeMztHjddxqjI4arSPjdjL0Wkbp6PE6ZrpQEuN19Dgd76aTstJwErb4U1wTugiXVqhsySbyPuHxeIr8XsMvJAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESOP8EKJycf+b8jiRAAiRwwQkcP34c06ZNs1pNAgMD8eijj+LgwYOIjIyEjLTYtm0bvvvuu3O+1l9++QU9e/a0PljuPGiS1XQi43MKP0bHu91ENZoYjSfSXsKGk7zGEzacaBFF2kpESHFK1WSy0pBKVroMycQ52XBSfA0nwdP3oFq99tb7QY0aNdhqcs7vsnwAEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABErgwBCicXBju/K4kQAIkcMEIvPbaaxg/frwlm0RERODll1/G7t27ERISgpSUFLz11lvFen3ff/892rRpY33I3HPUTY7SSeHlk1xjvE5e6vE6ucZYHd/U43TMsTpmqhE7G/Rtk+3pOF7HY4zXyZ96vI7HGK+js6jjdaTJJG+8DhtOYjPdiMvMgT1jjbXsseHEhejluumkNDecjJr3Hlr0TrDeA/72t78hNDQUX375ZbG+1/DBSIAESIAESIAESIAESIAESIAESIAESIAESIAESIAESIAEzh8BCifnjzW/EwmQAAlccAK7du1Cnz59rBE6N910E1544QUsXLgQI0eOxN13342vvvqqRK4zNzcXrVu3tj5w7jp0KthwouUSr3E663Kgx+fYcq0+n+iQsud7sOGk4GYTs/GEDSe6tSQyzSeXGetlLkQY51Yu1XsR9lzqQrisjZRz61iiz3tErEGFK2tbv/v16tXDzp07S+R9hg9KAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRAAiRw/ghQODl/rPmdSIAESOCCEsjOzrZaTfr27YsHH3wQR44cQWJiIkaNGqXEk5K+QJFOWrZsaX3w3DYgClM2uq2xOsXfcOIxGk+KoeFkff5mE2lFcWo3kT02nLgRn5WDuNUFZ9xqfbtXSqOJ7BtpbzYxm07YcKIbTcxmEzNLW8PJ0FkvolbTHtbvu7SayPtNcYzqKun3Kj4+CZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZDAmQlQODkzI96DBEiABMo8AbtsMnz4cDz77LP417/+pUZaTJgwAW+88cZ5e44ulwt+fn7Wh9DNrhmCydmfIHVzrmo88cpNHr2v0mOM0clLPUZHSyWpm/LkEhmZI7fJyBxzfI6Z9jE6KWcco5NjjNHJn1o0yTGEE51FlUzYcCKSiYzOsWWm2xijo1PEE+tYpc/t43Tk3PeQFhOz0cQ32XDi02xiNp0UQ8PJqDuOolXfybjk0sut3/PmzZtj37595+19ht+IBEiABEiABEiABEiABEiABEiABEiABEiABEiABEiABEig5AlQOCl5xvwOJEACJHBBCWzduhWdOnVS7SYjRozA888/r8ZZ9O7dGykpKTh+/Ph5vz6Px+M1XqeuXzckrnrXajrxkk4226WTXC2diJwiEsqmvNTySa7RaGJmnoRyJvlE2krkPio36JRz62DDCRLW5FiHtJfIOi91k0l8lney4cSFcRlujMvwyXRj7ZTpbkTLvj2XuxAtayOjJJd7N52UhoaTHhFZuOLKqy3RpGLFirjzzjvx3//+97y/z/AbkgAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJkAAJlCwBCicly5ePTgIkQAIXlMAzzzyDwYMHK9nkuuuuw549e7Bp0yY11mbGjBn49NNPL9j1ffPNN/D397c+mK5auwnGLdzn3XTChhMkrctB0toclRMdUvZ8j8S79J495TzfscbYs9Kt7pOwxo1EJZe4DcHEyCydIpVo2cRbLimMbOI1PkdaTXybTcw1G04sucSUTFRakokLWjrRKedRafo80p7SXFLQIW0mZrOJmUVsOBl8/R7UbNLN+n2W8TlBQUH48MMPL9h7DL8xCZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZBAyRKgcFKyfPnoJEACJHDBCLjdbtx8881KNhk6dCjuuOMOREVFqfW0adNw4sSJC3Zt5jf+6aefICN+5MNpOa6oXB0jZ+/0lk7YcIKkdR4tnqzzKLlE1lo+0TlxrZnFJ5+IUCKCikpbs4mstWxi5unlEzac+DSbmE0nTs0m0oTi22xirktpw0nIjYfQpMtY/O2SS6zf40aNGuHBBx80f82ZJEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEAC5ZQAhZNy+sLyaZEACZDAU089hcDAQHUkJSVh0KBBSjaZOHEi3n333VID6NSpU5g5c6b1YbWIJwERC7V0woYTNpwYTSex9lzlhqwn2FLOfY/xK/WeU8pe3uHS5yucM0b2V7hhzxhZZ7ggKeNyfLMkxuiUpoaTUXe8i5YByV6/t5UrV8b8+fPx22+/lZr3F14ICZAACZAACZAACZAACZAACZAACZAACZAACZAACZAACZBAyRGgcFJybPnIJEACJHBBCTz99NMIDg6Gn58fWrRooWST5ORkHD169IJeV0Hf/L777sNll11mfYDdssdoTFr3MVLZcMKGk8wcJZjEGRlrpbd0YkoovuKJrJ2kE3M/TzyxSSgrjHOfFLFEyycioNiODH0uoomWT8wsXw0nIpq0HzQbl1eqbv2uXnLJJYiNjYXH4yno15v7JEACJEACJEACJEACJEACJEACJEACJEACJEACJEACJEAC5ZAAhZNy+KLyKZEACZCAEDh27JhqDmnTpo2STeLj43HkyJFSDeell15C9ep5H2TXaNAWE9Je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TgZOsY92qW3DyeAZm2ju7jJNNqlMKZfIsTh8Xsolzmm8D7XvreFk4GT7tY9fdKbWDSfD5uw3ceT3r8+GE249MTaaGMfqzNzwkX5tubm56qsUCQIgAAIgAAIgAAIgAAIgAAIgAAL1RgDCSb2hxhuBAAiAAAiAAAiAAAiAAInxMa+//jodP35ciBcLFy4ULScslqjGj5pm//79aeLEicT/d/Py5ctFW8revXupuLiYnnjiCXr++eeJ3/P/s3cm4FWUZ/tXQRZlFyLKvu/7Grawhz0QshESAknYgsomCG4JexIgiawRrP3c2rr0q5YuarWgVcGvWLWiFkFtzjlJW23/1bp00fr8r+d9553MzHlncrJBEm6va657lrP+kmZOmd+5Hx7l8/HHHxOPrvnvf/97SX8cv/3tb2nXrl3ivfL7i4mJoQEDBohGmOHDh1NycjIVFhaKUUMtW7YUF9B6j02QwskDPFanhNaoPF4iRujwvgwxcseSx+Q6j9LhYyp5hE61jNdRY3csY3b6jE82LwCqC6O8z9ZsoppObM0mqumkcg0n6jlVckOJtd3ErdlE7bc2nCzZF9zYMnTWBm3DSYPG9tEk/PxV1XDCzSW6hhMWSazP2yysmxBRlJBSYflENZ04JJTqaDiJ2/5m0O8Ls5u+5kkhlizJD9jSXUIJGPJJac6/6zTNWPtTilj2AIUn5AsJRf1eWFPXcDLvjlO219WuX6St1US1myjJxC318omj9cSj4YRfJwsnlW04GTI3S9tw0mHgPNv7nHX7KUrgRhOWV9yaTdT+SjSc9J0SLEVUVcMJt5s4m1uatO5GPEYnlptN1MKSiabphOUTbkKx/o7weigNJ9PXngy6H8srqt3kcjWcjFvGwkmRq3TC43jmZ5Uen58ltzk7DVsc9J54v5ROSnP16PnEAAAgAElEQVRMim70jjwelVmaLIzwti57RgRzZ+GEb6sWq3wyOulR87X98pe/vKTndTwZCIAACIAACIAACIAACIAACIAACDABCCf4PQABEAABEAABEAABEACBGkKA20p+//vfE180On78OGVlZVF6ejrNmzePxo4dK9pOyiujeN2+f//+4nFnzZolGlRWr15NW7ZsIf6W9JEjR+ixxx6jn/3sZ/Sb3/yG3nzzTTp//jxxe8vf//53+ve//10hap9++il16dJFyDW9evUifg19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6E0nYSvkZSPmP01XdJ1k+W+VSy65hDIyMqrEZwQ/SAgnVealwgMFARAAARAAARAAARAAARAAARAAARAAARAAARAAARCoSQS+/fZb8ng8Qlh58cUXicWh++67j3bv3k3r168nbq9ZvHgxzZ8/n2bOnEkxMTGiIWP06NF09dVXU+/evSkgIIDatGlDl112mZAqykKgqE63wZLJ5ZdfLkSfFi1aUNOmTal+/fpUq1Yty8kf/TnziSAWg7p27So4T5o0iebMmSMkIh57c/DgQfrnP/9J7733Hn3zzTc16S2L51rNCPz8889ipJN6/0fdcoRYNBGyicrsfGOsjjXn83p2Puljdnht/l4fcc5zyXl75bol9/hIHLvlnnyay+t67vbRXD4W6ZO520dz+NiWvDZnl7xstp67fMTHbhsLJryup5RO9HYT3i++fIKGE3/ySZ4hnzgTDSceitXH7NiaTVTTCRpOPEIyYcFEbk7p5GzlE5ZVlGxiTymbyCYT1WyiUjWc6MnyCYsnjdsMsvy3B4/FPHHiRKX/ZIFwUulfIjxAEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEChbAty+8sUXX4gGlg8++IBOnTpFL7/8shAleFQQt7Hs37+fuIVjz549opWFxzGlpqbSzTffLMa+cO07t3XMmzePkpKSaMaMGRQfH0/R0dE0efJk0dwyduxYGjlypGhw4RaXc7mNGDGCQkJCxP3yeKO4uDjxmGbPnk0LFiwgfrw33XSTEEPS0tLEt4d79Oghxk2xrDNkyBBq164d9e3bVzyHzZs309///nd69913KT8/H+NtyvYtiFurAgT491rJJiHz9lFKjk/KJtkFmSLG6simE10uUbKJLpm4yiZFSiYslLB0YqQQTOS+lE0MqWSPLW1yiZJNdMnEIpu4CiZ5hlziTLdmk5JIJmg48SeZOJtNVOOJ3mzCa6LdBA0nfsbooOFENZuorKwNJ6rpRGXvKTvogouamX97+W/wddddR999912l/dSAcFJpXxo8MBAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAgXNJgNtJWrduLdphxo8fT3PnzqXMzEwhzqSkpBCLKTwO6ccffzyXDwO3DQKVjsBrr71mnvAMHDbdbDZBwwkaTmZm5dEM2zY9U65Nz/SKyzh5zV/yyBy+XM9p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dr10HjBRug6aLpcMy5SeVxVIn1Fz5bIxi6Xf9bfIgAkrZcCEFfWr4737j18hfccuk95Xz5XuV+RJ18EzpFO/G+XCXteY52/TaYC0atdDzmrdUc5o2eaozdq1ayfjxo2TO+64Q7797W+bQFEz+vXkrdYiQOCkFhxuQgABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBovAIfffSRVFVVyR//+Ef53e9+Jy+++KI8++yzJrjyve99Tw4ePCj333+/lJaWmgDLypUrZfHixSbEkpGRIRMnTpRrr71WBgwYIF26dDkhXVeORximvo/ZqVMn41BcXCwbNmww3Wleeukl0VFK/EOgJgECJzXJsI8AAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggg0CwFPv74Y9HxQG+88Ya88sor8txzz8mPfvQj+c53viN79uyRzZs3y5o1a+TOO++U2267TZYsWSLz5s2TgoICyczMlJtuusmMDxo7dqyMGDFCBg8eLH379pXu3buLhjvatm0r55xzzlF3HQkGSs4991y56KKLzGP3799fhg8fLmPGjJFJkyaZ1zJnzhzz+latWiVr166VvXv3mveigZJ33323WX6+vOn0CBA4SY8jj4IAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCDQbAQInzeaj5o0igAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAALpESBwkh5HHgUBBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEGg2AgROms1HzRtFAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQTSI0DgJD2OPAoCCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIINBsBAicNJuPmjeKAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgikR4DASXoceRQEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQKDZCBA4aTYfNW8UAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBIjwCBk/Q48igIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggECzESBw0mw+at4oAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCQHgECJ+lx5FEQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAIFmI0DgpNl81LxRBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAgPQIETtLjyKMggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAALNRoDASbP5qHmjCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIBAegQInKTHkUdBAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQSajQCBk2bzUfNGEUAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQACB9AgQOEmPI4+CAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgg0GwECJ83mo+aNIoAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAAC6REgcJIeRx4FAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBoNgIETprNR80bRQABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEE0iNA4CQ9jjwKAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCDQbAQInDSbj5o3igACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIpEeAwEl6HHkUBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEECg2QgQOGk2HzVvFAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQSI8AgZP0OPIoCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIBAsxEgcNJsPmreKAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgggkB4BAifpceRREEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQACBZiNA4KTZfNS8UQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAID0CBE7S48ijIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACzUaAwEmz+ah5owgggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAQHoECJykx5FHQQABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEmo0AgZNm81HzRhFAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAgfQIEDhJjyOPggACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIINBsBAifN5qPmjSKAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAukRIHCSHkceBQEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQaDYCBE6azUfNG0UAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBNIjQOAkPY48CgIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggg0GwECJw0m4+aN4oAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCKRHgMBJehx5FAQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAoNkIEDhpNh81bxQBBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEiPAIGT9DjyKAgggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAQLMRIHDSbD5q3igCCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIJAeAQIn6XHkURBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAgWYjQOCk2XzUvFEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQCA9AgRO0uPIoyCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAs1GgMBJs/moeaMIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggEB6BAicpMeRR0EAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBJqNAIGTZvNR80YRQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAIH0CBA4SY8jj4IAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCDQbAQInzeaj5o0igAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAALpEfj/DWmTma3AtSYAAAAASUVORK5CYII=" style="margin-left: auto; margin-right: auto;" width="723" /></td></tr><tr><td class="tr-caption" style="text-align: center;">external DSL pipeline</td></tr></tbody></table><p><span style="font-family: verdana;"></span></p><p><span style="font-family: verdana;"><br /></span></p><p><span style="font-family: verdana;">Loading a DSL file is a trivial thing , it can be uploaded to the system using a web interface. </span></p><p><span style="font-family: verdana;">Once a file is uploaded then it needs to be parsed, this is where things become little interesting and many built in libraries are available to parse files and extract tokens but in this example we will do simple parsing and code generation.</span></p><p><span style="font-family: verdana;">For parsing template files that will be used, content of the DSL file is put in template file before compilation. </span></p><p><span style="font-family: verdana;">For this example I am going to use the template file.</span></p><p><span style="font-family: verdana;"><br /></span></p>
<script src="https://gist.github.com/ashkrit/8627da54f1bfb267be85cd0b28ed9b25.js"></script>
<p><span style="font-family: verdana;">Above template file has 2 variables <i><b>$REPLACE</b></i> & <i style="font-weight: bold;">//CODE, </i>these two variables are replaced during the parsing and code generation phase.</span></p><p><span style="font-family: verdana;">$REPLACE - will contain some random value to give a unique name to the block of code that needs to be compiled. </span></p><p><span style="font-family: verdana;">//CODE - will contain DSL code submitted by the user.</span></p><p><span style="font-family: verdana;">Once tokens are replaced we are ready for compilation.</span></p><p><span style="font-family: verdana;">For compilation we will use <a href="https://draft.blogger.com/blog/post/edit/6543397255913469784/8283799606077935045#" id="gmail-https://docs.oracle.com/javase/8/docs/api/javax/tools/JavaCompiler.html">JavaCompiler</a> API, this api gives a programmatic interface to JVM compiler. Very powerful API for doing something in running VM.</span></p><p><span style="font-family: verdana;"> Code snippet for compiling code using JavaCompiler interface.</span></p><p><span style="font-family: verdana;"><br /><script src="https://gist.github.com/ashkrit/9ea9783c43f6667cccfc4bd8cbb06d69.js"></script></span></p><p><span style="font-family: verdana;">Once code is compiled then using the java reflection rule can be loaded.</span></p><p><span style="font-family: verdana;">Code snippet for DSLLoader </span></p><p><span style="font-family: verdana;"><br />
<script src="https://gist.github.com/ashkrit/b39a822be29aef7dc30427d266ad184e.js"></script>
</span></p><p><span style="font-family: verdana;">Once the rule is loaded from DSL then it is straightforward to update the current VM to use the newly created rule.</span></p><p><span style="font-family: verdana;">DSL code is type safe, all the checks are done at compile time and it is highly optimized code because it has no reflection or other magic, so it will benefit from all the hotspot compiler.</span></p><p><span style="font-family: verdana;">For DSL creator perspective these rules can be tested in sandbox before using it production.</span></p><p><span style="font-family: verdana;">Few things that can be improved are error messages when DSL has compilation error, right now it is just showing java error messages and it will not be that user friendly for non java programmers. </span></p><h3 style="text-align: left;">Trading System</h3><div><span style="font-family: verdana;">In earlier post below internal DSL was used.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "jetbrains mono", monospace; font-size: 9pt;"><span style="font-style: italic;">newOrder</span>(<span style="font-style: italic;">equity</span>(<span style="color: #6a8759;">"IBM"</span>)<span style="color: #cc7832;">, </span>(order<span style="color: #cc7832;">, </span>ts) -> {<br /> order<br /> .buy(<span style="color: #6897bb;">100</span>)<br /> .at(<span style="color: #6897bb;">126.07d</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span>ts.execute(order)<span style="color: #cc7832;">;<br /></span>})<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="font-style: italic;">newOrder</span>(<span style="font-style: italic;">equity</span>(<span style="color: #6a8759;">"GOOG"</span>)<span style="color: #cc7832;">, </span>(order<span style="color: #cc7832;">, </span>ts) -> {<br /> order<br /> .sell(<span style="color: #6897bb;">100</span>)<br /> .at(<span style="color: #6897bb;">1506.85</span>)<br /> .partial()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span>ts.execute(order)<span style="color: #cc7832;">;<br /></span>})<span style="color: #cc7832;">;</span></pre></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">Lets have external DSL for this.</span></div><div><span style="font-family: verdana;"><br /></span></div><div><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "jetbrains mono", monospace;"><span style="color: grey; font-size: x-large;">buy 100 IBM stocks at 122.06</span></pre></div><div><span style="font-family: verdana;">This has few keywords(i.e<i> buy, stocks , at</i>) related to trading domain and also has few parameter related to order like 100 (i.e <i>unit</i>) , IBM ( i.e <i>stock</i>) , 122.06 ( i.e <i>price</i>).</span></div><div><span style="font-family: verdana;">For this example it is easy to write as parser that will create order object.</span></div><div><span style="font-family: verdana;">Refer to <a href="https://github.com/ashkrit/dsl/blob/master/src/main/java/org/domainspecific/dsl/trading/external/Parser.java" id="https://github.com/ashkrit/dsl/blob/master/src/main/java/org/domainspecific/dsl/trading/external/Parser.java" name="https://github.com/ashkrit/dsl/blob/master/src/main/java/org/domainspecific/dsl/trading/external/Parser.java" target="_blank">Parser</a> for more details on how text is parsed and order object is created.<br /></span></div><div><span style="font-family: verdana;"><br /></span></div><div><span style="font-family: verdana;">All the code used in this post is available at <a href="https://github.com/ashkrit/dsl" id="https://github.com/ashkrit/dsl" name="https://github.com/ashkrit/dsl" target="_blank">DSL</a></span></div><div><span style="font-family: verdana;"><br /></span></div>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com0tag:blogger.com,1999:blog-6543397255913469784.post-36413869886366540982020-08-11T02:40:00.008-07:002020-08-11T04:36:06.246-07:00Speak the language of the domain<p>In this post I will share about what DSL and how we can create our own.</p><p>Domain Specific languages are specialized languages targeted to domain, it has less vocabulary as compared to general purpose languages. DSL is not new concepts it has been around for ages for eg <a href="https://en.wikipedia.org/wiki/Cascading_Style_Sheets" id="gmail-https://en.wikipedia.org/wiki/Cascading_Style_Sheets" target="_blank">Cascading Style Sheets</a> is one of the very popular one that we get exposed to everyday, excel spreadsheet macros, SQL,Build tools like make , Ant etc and many tools in unix like awk,sed etc.</p><p>XML can be also categorized as DSL but not sure how many people will love that! </p><p>DSL maps <i>problem domain</i> to <i>solution domain</i>. </p><p><img alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAtIAAAHSCAYAAADBgiw3AAAgAElEQVR4AeydB9hVxbX+TSwUC9JFkN4UQRCUIkUsNEVAuoCAXYw9KhgLaBQ7GDXWxNxE440msaU3y00xVVOMKZYkoslNuUlM8k/P/J/fnG9937DZ+/Syzznvfp555py9p76zZuadtdfM3snpEgJCQAgIASEgBISAEBACQqBgBHYqOIYiCAEhIASEgBAQAkJACAgBIeBEpCUEQkAICAEhIASEgBAQAkKgCAREpIsATVGEgBAQAkJACAgBISAEhICItGRACAgBISAEhIAQEAJCQAgUgYCIdBGgKYoQEAJCQAgIASEgBISAEBCRlgwIASEgBISAEBACQkAICIEiEBCRLgI0RRECQkAICAEhIASEgBAQAiLSkgEhIASEgBAQAkJACAgBIVAEAiLSRYCmKEJACAgBISAEhIAQEAJCQERaMiAEhIAQEAJCQAgIASEgBIpAQES6CNAURQgIASEgBISAEBACQkAIiEhLBoSAEBACQkAICAEhIASEQBEIiEgXAZqiCAEhIASEgBAQAkJACAgBEWnJgBAQAkJACAgBISAEhIAQKAIBEekiQFMUISAEhIAQEAJCQAgIASEgIi0ZEAJCQAgIASEgBISAEBACRSAgIl0EaIoiBISAEBACQkAICAEhIAREpCUDQkAICAEhIASEgBAQAkKgCAREpIsATVGEgBAQAkJACAgBISAEhICItGRACAgBISAEhIAQEAJCQAgUgYCIdBGgKYoQEAJCQAgIASEgBISAEBCRlgwIASEgBISAEBACQkAICIEiEBCRLgI0RRECQkAICAEhIASEgBAQAiLSkgEhIASEgBAQAkJACAgBIVAEAiLSRYCmKEJACAgBISAEhIAQEAJCQERaMiAEhIAQEAJCQAgIASEgBIpAQES6CNAURQgIASEgBISAEBACQkAIiEhLBoSAEBACQkAICAEhIASEQBEIiEgXAZqiCAEhIASEgBAQAkJACAgBEWnJgBAQAkJACAgBISAEhIAQKAIBEekiQFMUISAEhIAQEAJCQAgIASEgIi0ZEAJCQAgIASEgBISAEBACRSAgIl0EaIoiBISAEBACQkAICAEhIAREpCUDQkAICAEhIASEgBAQAkKgCAREpIsATVGEgBAQAkJACAgBISAEhICItGRACAgB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rXuVXAgIASEgBISAEBACQqAIBESkiwBPrwoBISAEhIAQEAJCQAikFwER6fTWvUouBISAEBACQkAICAEhUAQCItJFgKdXhYAQEAJCQAgIASEgBNKLgIh0euteJRcCQkAICAEhIASEgBAoAgER6SLA06tCQAgIASEgBISAEBAC6UVARDq9da+SCwEhIASEgBAQAkJACBSBgIh0EeDpVSEgBISAEBACQkAICIH0IiAind66V8mFgBAQAkJACAgBISAEikBARLoI8PSqEBACQkAICAEhIASEQHoREJFOb92r5EJACAgBISAEhIAQEAJFICAiXQR4elUICAEhIASEgBAQAkIgvQiISKe37lVyISAEhIAQEAJCQAgIgSIQEJEuAjy9KgSEgBAQAkJACAgBIZBeBESk01v3KrkQEAJCQAgIASEgBIRAEQiISBcBnl4VAkJACAgBISAEhIAQSC8CItLprXuVXAgIASEgBISAEBACQqAIBESkiwBPrwoBISAEhIAQEAJCQAikFwER6fTWvUouBISAEBACQkAICAEhUAQCItJFgKdXhYAQEAJCQAgIASEgBNKLgIh0euteJRcCQkAICAEhIASEgBAoAgER6SLA06tCQAgIASEgBISAEBAC6UVARDq9da+SCwEhIASEgBAQAkJACBSBgIh0EeDpVSEgBISAEBACQkAICIH0IvD/AffxHaVMV+O/AAAAAElFTkSuQmCC" /></p><p>DSL are built using general purpose language and are targeted for specific domain like </p><p></p><ul style="text-align: left;"><li>Accounting domain</li><li>Insurance domain</li><li>Trading domain</li><li>Testing etc</li></ul><div>DSL is about simplification or removing friction required to solve the problem, good DSL will enable all stakeholders of the product to participate in software development. </div><div> </div><h2 style="text-align: left;"><span style="font-size: xx-large;">DSL Categories</span></h2><h4 style="text-align: left;"><span style="font-size: x-large;">Internal</span></h4><div><br /></div><div>Internal DSL are written in the same language as host language and use host language compiler to produce executable that is executed by language runtime</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><img alt="" height="348" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA34AAAGyCAYAAAC7nBbyAAAgAElEQVR4Ae2dfcw1R12/iX8hIC2CUgKhJYiARFshWBCwFYSSH8GnxJciAVslCqKhT4xK1WArGqkQ0/oGRNQ+ROXFGKhvqaJJHyEKvhYhiEK0hmAwRHwhKH/uL9eBz9N5tnvOfe777O6ZnXNNcu4999ndme9cMzs7n/nOzt6nM0hAAhKQgAQkIAEJSEACEpBA0wTu03TuzJwEJCABCUhAAhKQgAQkIAEJdAo/K4EEJCABCUhAAhKQgAQkIIHGCSj8Gi9gsycBCUhAAhKQgAQkIAEJSEDhZx2QgAQkIAEJSEACEpCABCTQOAGFX+MFbPYkIAEJSEACEpCABCQgAQko/KwDEpCABCQgAQlIQAISkIAEGieg8Gu8gM2eBCQgAQlIQAISkIAEJCABhZ91QAISkIAEJCABCUhAAhKQQOMEFH6NF7DZk4AEJCABCUhAAhKQgAQkoPCzDkhAAhKQgAQkIAEJSEACEmicgMKv8QI2exKQgAQkIAEJSEACEpCABBR+1gEJSEACEpCABCQgAQlIQAKNE1D4NV7AZk8CEpCABCQgAQlIQAISkIDCzzogAQlIQAISkIAEJCABCUigcQIKv8YL2OxJQAISkIAEJCABCUhAAhJQ+FkHJCABCUhAAhKQgAQkIAEJNE5A4dd4AZs9CUhAAhKQgAQkIAEJSEACCj/rgAQkIAEJSEACEpCABCQggcYJKPwaL2CzJwEJSEACEpCABCQgAQlIQOFnHZCABCQgAQlIQAISkIAEJNA4AYVf4wVs9iQgAQlIQAISkIAEJCABCSj8rAMSkIAEJCABCUhAAhKQgAQaJ6Dwa7yAzZ4EJCABCUhAAhKQgAQkIAGFn3VAAhKQgAQkIAEJSEACEpBA4wQUfo0XsNmTgAQkIAEJSEACEpCABCSg8LMOSEACEpCABCQgAQlIQAISaJyAwq/xAjZ7EpCABCQgAQlIQAISkIAEFH7WAQlIQAISkIAEJCABCUhAAo0TUPg1XsBmTwISkIAEJCABCUhAAhKQgMLPOiABCUhAAhKQgAQkIAEJSKBxAgq/xgvY7ElAAhKQgAQkIAEJSEACElD4WQckIAEJSEACEpCABCQgAQk0TkDh13gBmz0JSEACEpCABCQgAQlIQAIKP+uABCQgAQlIQAISkIAEJCCBxgko/BovYLMnAQlIQAISkIAEJCABCUhA4WcdkIAEJCABCUhAAhKQgAQk0DgBhV/jBWz2JCABCUhAAhKQgAQkIAEJKPysAxKQgAQkIAEJSEACEpCABBonoPBrvIDNngQkIAEJSEACEpCABCQgAYWfdUACEpCABCQgAQlIQAISkEDjBBR+jRew2ZOABCQgAQlIQAISkIAEJKDwsw5IQAISkIAEJCABCUhAAhJonIDCr/ECNnsSkIAEJCABCUhAAhKQgAQUftYBCUhAAhKQgAQkIAEJSEACjRNQ+DVewGZPAhKQgAQkIAEJSEACEpCAws86IAEJSEACEpCABCQgAQlIoHECCr/GC9jsSUACEpCABCQgAQlIQAISUPhZByQgAQlIQAISkIAEJCABCTROQOHXeAGbPQlIQAISkIAEJCABCUhAAgo/64AEJCABCUhAAhKQgAQkIIHGCSj8Gi9gsycBCUhAAhKQgAQkIAEJSEDhZx2QgAQkIAEJSEACEpCABCTQOAGFX+MFbPYkIAEJSEACEpCABCQgAQko/KwDEpCABCQgAQlIQAISkIAEGieg8Gu8gM2eBCQgAQlIQAISkIAEJCABhZ91QAISkIAEJCABCUhAAhKQQOMEFH6NF7DZk4AEJCABCUhAAhKQgAQkoPCzDkhAAhKQgAQkIAEJSEACEmicgMKv8QI2exKQgAQkIAEJSEACEpCABBR+1gEJSEACEpCABCQgAQlIQAKNE1D4NV7AZk8CEpCABCQgAQlIQAISkIDCzzogAQlIQAISkIAEJCABCUigcQIKv8YL2OxJQAISkIAEJCABCUhAAhJQ+FkHJCABCUhAAhKQgAQkIAEJNE5A4dd4AZs9CUhAAhKQgAQkIAEJSEACCj/rgAQkIAEJSEACEpCABCQggcYJKPwaL2CzJwEJSEACEpCABCQgAQlIQOFnHZCABCQgAQlIQAISkIAEJNA4AYVf4wVs9iQgAQlIQAISkIAEJCABCSj8rAMSkIAEJCABCUhAAhKQgAQaJ6Dwa7yAzZ4EJCABCUhAAhKQgAQkIAGFn3VAAhKQgAQkIAEJSEACEpBA4wQUfo0XsNmTgAQkIAEJSEACEpCABCSg8LMOSEACEpCABCQgAQlIQAISaJyAwq/xAjZ7EpCABCQgAQlIQAISkIAEFH7WAQlIQAISkIAEJCABCUhAAo0TUPg1XsBmTwISkIAEJCABCUhAAhKQgMLPOiABCUhAAhKQgAQkIAEJSKBxAgq/xgvY7ElAAhKQgAQkIAEJSEACElD4WQckIAEJSEACEpCABCQgAQk0TkDh13gBmz0JSEACEpCABCQgAQlIQAIKP+uABCQgAQlIQAISkIAEJCCBxgko/BovYLMnAQlIQAISkIAEJCABCUhA4WcdkIAEJCABCUhAAhKQgAQk0DgBhV/jBWz2JCABCUhAAhKQgAQkIAEJKPysAxKQgAQkIAEJSEACEpCABBonoPBrvIDNngQkIAEJSEACEpCABCQgAYWfdUACEpCABCQgAQlIQAISkEDjBBR+jRew2ZOABCQgAQlIQAISkIAEJKDwsw5IQAISkIAEJCABCUhAAhJonIDCr/ECNnsSkIAEJCABCUhAAhKQgAQUftYBCUhAAhKQgAQkIAEJSEACjRNQ+DVewGZPAhKQgAQkIAEJSEACEpCAws86IAEJSEACEpCABCQgAQlIoHECCr/GC9jsSUACEpCABCQgAQlIQAISUPhZByQgAQlIQAISkIAEJCABCTROQOHXeAGbPQlIQAISkIAEJCABCUhAAgo/64AEJCABCUhAAhKQgAQkIIHGCSj8Gi9gsycBCUhAAhKQgAQkIAEJSEDhZx2QgAQkIAEJSEACEpCABCTQOAGFX+MFbPYkIAEJSEACEpCABCQgAQko/KwDEpCABCQgAQlIQAISkIAEGieg8Gu8gM2eBCQgAQlIQAISkIAEJCABhZ91QAISkIAEJCABCUhAAhKQQOMEFH6NF7DZk4AEJCABCUhAAhKQgAQkoPCzDkhAAhKQgAQkIAEJSEACEmicgMKv8QI2exKQgAQkIAEJSEACEpCABBR+1gEJSEACEpCABCQgAQlIQAKNE1D4NV7AZk8CEpCABCQgAQlIQAISkIDCzzogAQlIQAISkIAEJCABCUigcQIKv8YL2OxJQAISkIAEJCABCUhAAhJQ+FkHJCABCUhAAhKQgAQkIAEJNE5A4dd4AZs9CUhAAhKQgAQkIAEJSEACCj/rgAQkIAEJSEACEpCABCQggcYJKPwaL2CzJwEJSEACEpCABCQgAQlIQOFnHZCABCQgAQlIQAISkIAEJNA4AYVf4wVs9iQgAQlIQAISkIAE5iHwX//1X93dd989T2KmIoFjElD4HROYh0tAAhKQgAQkIAEJHBaBs2fPdrfffnt30003dadPn+6uvPLK7rLLLuvuc5/7HPnhuKuvvrq79dZbFYWHVW2qy63Cr7oi0SAJSEACEpCABCQggRoIbCvuIgAvvvji7tJLL90oBi+55JLuzjvvrCF72nBgBBR+B1bgZlcCEpCABCQgAQlIYDsCEXFXXHFFx+fGG2/sbrnllpVwu+uuu7aKhKmft912W3fttdd2CMOIxOuuu26r8z1IAmMRUPiNRdJ4JCABCUigOQI8r8MULz5nzpxZTfNiqlc+TPfKh05cfmfLtDDOM0hAAnUSKK/vcipneR3n+s629ADy20kCwvGCCy5YCUDiww6DBOYgoPCbg7JpSEACEpBAtQTS+Yuw41kcOnQZlR9jy9QungtCDBokIIHxCWSAhm0p3BiQGRJtY1zXp06dOnFG8BbG+6f4OzFGTzwmAYXfMYF5uAQkIAEJLJdARB4dQzqDF1544UaBx6h8pngxTYtpXuWH53TyYSpXuS/n9TuYpIm4RGg60r/cuqTl0xNAHPU9cRmY2eb67V97/f/L65vrFSFXXsN8z/Wd7bbTO7ehw/WfqaSKv22IecyuBBR+uxL0fAlIQAISqJYAHSu8bIz643Xrd/zyP52+CLt3vetdoy+8QGeRTmQ6eUmXLR3ZMTuT1RaGhkmgIMBzb6Woy0qZJ/W2Z6CF7fXXX39OwDEgM4VoK7Ky01faqHj+fOZvJ5SevAUBhd8WkDxEAhKQgASWQ4COFMumD3UgM8KPCEPg7eN9W6TJMz54F0oBSKdvH/Ysp2S1dGkEIu64HuNlL5+RK+v/0HcEUd8TVwo5rvUWAgM/eeaPtsEggakIKPymIntg8dJotdIAH1jRmV0JNEOAdgjx1O9AIrDoTNXoVaPdRITGZqaB0kG2PW2mWh5ERqivebaOa3Bo0CV1PNsMwmR6JddoPHMHAa2XSQaiwobvBglMQUDhNwXVA4qzP3LHVKoaO1cHVCRmVQIHSYBpYuk0saUzuaTOE54RppomDwhAngE0SKA2AqXIY5rypinU8dhl6iXCzj7C+hLNIBDXv5zWc3LPyQko/E7OzjO77tzzKjy3kmkKdFyY1mGQgAQkMAeBcgCKjtOSp0vSMWZqWwQgebMDOEctMo1NBOLNW+fJi/cOgRfPnV7rTUTX78sAEIJahus5uedkBBR+J+O281kZMaMxzSdz4MtliIe+c1zOGXM7RueCRj8dFkasuEkw7WMoH9v+xqg3+RzDvp0LzggkIIGqCNDJpM1h8KmlNgJvZRZ8oC3luSaDBOYiQB+Fey8ePepf7uvZZgEV6mVL191cfDelA/ssAkUfyiCBMQko/MakeURcNI5MR9o0LSKNqtv73OtGEybchLgZIRwRhAYJSOBwCcQ7tqRpnduWFh3AjP7T/jGIxm8GCUxBoBR7ud9mixBhYJfrzDo4Bf3z44RxZlEh/mR+Ph//OzkBhd/J2W115iaxR4el/GQOPFOVNn04rjxvrO8ZYUpDP8eWNDfllX10fMjjJvuYDoWoVghuVS09SALNEIg3ouWOEV6VdAJ9jrqZqltNRpgazaBCrqXc+3lOlrq35KnT1UA+gSH0H3Pd08dpuY07AR5POSEBhd8JwR11Ghdsfy4803YQbewznJwANyFuRhHAuUllG48g01S8YZ2cs2dKoAYCXMPllHbeyVdOE891T3tbfsqZFXSalh64b2TwizbO+8jSS3T/9nOP7PdTGGTl/qrI2H/5YEF53dOO8QywQQK7EFD47UJv4Fway3J1OUZrFHsDoEb+icYQzukYpTPIlsaS5yK9kY0M3egksCUBrr1SvJXCjfZynWArr+NdvuO5aCHAkbzAAvHX4vTWFsqp5jxQh7gflgMj9FOYWeNAaZ0lR5mVfRvaTH4zSOAkBBR+J6G25hwa03KqBELEi3MNrAl/jkeQDlKmSaTTyLOBjHJaLhMWgFE3T4DrJ0KO6ylCLu/vQsiVHctcfyfZZjn4TGnnuu5PD8dDweBP+Wm5E1s+90feDRI4igDXQ386J9eW3r2jyNWzn3YvbSh9TfqcBgkcl4DC77jEBo7HFY9XKRckHZSWOx0DCKr+iRtbRslTRmwRgUwbM0hAAvcQoO1C1EXQlR65cmCrvJaO+s4ATIQb21K4Zen3iDbbznvKYtM3BhbDXfG3idRh7+O6GprOqbd4mfWC/iZtaK59Bthoqw0S2JaAwm9bUmuOo/FMZ4jRMxpZQ50E8FIMiUAaTjwWdjjrLDetGpdAvHURdgyA0DFMO5YOxaZtKeRKDxzXlwJu3PLaFBu8U04+87eJ1OHtoz6Ugs/pnG3VAfqe9Dlz/SsA2yrfKXOj8NuBLm72XHRMvXH64A4wZz4VkYfXoWw4KUu9gDMXhMlNRiACj3YqUzDTXm3ack0wokybFs+cYm6yYto54kz7RLgr/nbG2UQEDGTmGkfwcR3bP2miaO+VCQZ/yn6MAvBeiPyhR0Dh1wOy7b90pNKwMlXJsFwCjJz1p4LSeNJh9ma53HI9JMsj8ujwbeO9Q9jFUxcvnR7v5daYUvxZjsstx10tpx0oHztxnYFdiS7n/HUC0D7McspwLksVficgXYo+LjZDGwToMCHiy9EzxD3l7Uh6G2XcSi64mTNVk+fvyo5eBqOyReDR+ctzdHYCWqkB985HnvtB+BsOjwD3qEzXZgVIHzs5vDpAjvsCkDrBgKBt/2HWh6FcK/yGqGz4LaKP6ROKgQ2gFr5ryAtIh8qHqBdesAs2n/aGG/g6oReRR93V67Pggj6h6XTsuC8h+p2FckKICz2tL/rs5C+0IEc0m/tABoNoExCADBR6bxgR8kKjUvgdo+CYJ5+RdEXfMcAt+FAaSTwm6VBR/s6hX3CBLsx0Vp1lsCkj+Wl/2GaqpiP7CyvUCc2lLqSOeI+aEHRFUZeiz7UGKiqYSkyhTSgFIO0D9xQFYCUFtAczFH5bQsd9nhuq0zu3hNbQYYyg9qdQIAAZVTNIYEwCdOSGxB5TkBmEsM6NSbu9uKgj3KvwDBvaJtAXfW3n1tztQgChl2eB05f1RfC7EF3uuQq/LcqOxjUXiqJvC2CNH9IXgEwBdfSs8UKfOHsMLLCYEIMJaWvYIvaYtmf9mrgAGoqeupTnlB0kaKhge1mhnDPtG++/QQLbEOgLwDwDuM25HtMGAYXfEeXIRZJpVoykGiQQAgjATAGljtBxN0jgOARoXxh1TRsTsUdbw4CTQQInIUDbRF1yoZeT0Kv/nFL0sZAL/xskcBwC3HvKKaAMInjPOQ7B5R6r8NtQdmXj6ojaBlAHvIs6Uk6fYIqeN+EDrhBbZp3nLqgrpXePm7Aemi0BethGArRBGZSyM7cR1SJ38r5Z2g5F3yKLryqjuRdlhgB1igXEDG0TUPhtKF8b1w1w3HUeATrs6Wgxcqb4Ow+P/3yBAKvCZnpWRB8DB3bOrSJjE8iAlCt8jk12v/FRnrQd3G+majc++clPdq997WtXg1MIgxpDBs/e+MY3dp/73OdqNHFrm+g/POUpT1kx3/qkkQ8sFy/U+zcy3MqiU/itKZA5Gtc1SfvzQglwE2YElpuy4m+hhTiB2QwCMIpaPr9Hp40brQMEEwA3yhWBTPd0tko7FYJ7TAaMppodQJt0ww03dPe9731XHwRgbQHRxzRmWLzwhS/sPvKRj9Rm4tb2vO997+ue+9znrqb773uQpvT+8fgBbYihPQIKv4EynaNxHUjWnxogwE0z4s/naxoo0B2zgIev//yeN9MdoXr6VgR4hoeOMQMOhuUT4N6StmTK9QbwoF100UXnBGZtU/+o1+U0eUQT4mmJAU8lfBHZ9Bdq8K5SzzJbgPbDx1eWWLM226zw6/Gh0mdkfsrGtZes/zZEgIGDTPuk0TQcHgFu4OWUTp7fq+Gmfnglcdg5jnfosCm0kft4uGhLpgq0UaSDELnmmmtWAmDfXqgyrwglPJAIYOxEoDBzAjG4xBDeXKd4WWuassoAZfoxzmBaYu1ab7PCr8cmIx14bQwSOCmBUvw5gHBSiss7jw5IOmjczHlofqopWcujo8VzE8iiDQxoGpZLIM9f0RGfqiyJlxWG4+Wh3SLdmtqvCCUG5/FMIpZqE0zb1rLS24ewqolz8kA/JjOYYM7/huUTUPgVZUijQqNH41rTCBINBHPY/+iP/qh705vetJoawJb/z549u9pX00hRgXT0r+STsuHh89oDjST1iQ91y9AugbLTlDaETpNBAvskkOXabX/2WQq7pT3XfeRtb3tb97jHPW61yMgdd9yxElYIwVqen0sbizfyVa961eqeyha7lxjybB/3i5pfpA73Uvzxv2HZBBR+Xyg/KnOmeNY0taFsHCIi2GIrN3U+V1111eolz0sQQ7tcLpQRo3sZ7dslrrnOzUgtU1NsMOeiPm86vL8xz95wbTJrwLKetwxMbZiAwm+Yy5J+zZTxKWeOMJjKYwmIKu5Z9CUQVTUt7IJHDBbMqGAgg34agqmmQfrj1Cvs575Rq7evzAv3s4g/7DUsm4DC7wvlR6NKp40bZU2BRo0Rt3j02DLF4RAXiCDPLHl8+eWXLyr/6XzxehBDOwTofGSwKG3HUjsh7ZSKOSkJpO3R41dSmeY7HWPaA8TIWFPiMnDIlN0pB5O4t2I79yhsR2TRJ6ql3iBEX/7yl58Tptj1spe9bFH9gLLWwTivCyNfSxi0p/5l6rizWcrSXN53hV/XrUaM6Ljxqb3jRoPBRTfGfHBEJFM6amncN10+eD5ZlpxRSG5SY+R/U3pj7qPBzEPSNXmTx8zjIcVFeZarynEzXMI1dEhlZF4/T0DhN19NiEckfYldRSD3+sQ1ZftCn4f2DO8T9ydECKKPfkYGnOejOJxSKUxhgW18phTDw5aM82vyQx3h+1IC7KmT1JXa+8pLYboPOxV+XXdu6VqmaNUeEDyInzGWLyaufb80dBve3HxY8pgRMubzYzc3xSUFbM5N3AZzSSV3vq3UO27WlCVins6HQQK1ElD4zVsytA+IpnhG0ubTZvBql+OEOaZ4Yk9e3xBvH0IEL9QYfYzj5HfdsX1vX6Z41vLs4Tq71/2OWMUrTN1AcC+tP8AAPLZTXwzLJHDwwo+LLo3zEi7ANHq72sr5NDpp7Guuvowy0diQdzyUiKhaRiKPwy3TiXlGwbA8AlwzeZaPDvWu1+DyCGjx0gikvi7VM7I03qW9EYGZ7UE/g/JgEPOotoMBJY7HizhliKjCLu6v2MwUSsRgLYH7PSKYQWr6K4hS7FxqoA9DXnhXYk2ct+VJ3U2dntITva09Hnd8Agcv/NLALsHbh9hhcZMxXqhKI88oJNuaQ0bHaPBpZLgJLLWxIS9pMMmHYVkEMgK/hLZiWWS1dioCGdScKn7j3Y4AXrR4X1Mm6+7jdKxzzNT3uogqXoKOIKF/gTeqlmfOcv+HB+KUweoliz5qCwvmsIjOEgbd19XuJfWb1+XhkH8/eOGXaVtTN7BjVDKmNjAat6tYo+Gk0TnuNAMaYW4Ic3rbKJd4+7hJMdVzzvTHKLcyDjoA3MSod/A0LIMA1wzlNvUiCzXS4JqnU8inlg7hmJzoaDOtrbW80b5EQIzJy7hOToD7WSkAGUzqC5m8B5QZIlOGDCRTRxB7CJLavGnwggdCib7PUZ7STby4HrjOeRUWnzmmimIvU3xvvvnmVdrpe8XDusnemvdlcIJ8GJZH4KCFXyovXpglBDpeeBt2fW8N0wuYarDpoWJuCnSESnHC8dwc5uogYQOjoi984QtXnj6EX/8muYRy69uYG7/Ph/XJ1Ps/gy10kA7F28e1x/VGO8G7veh05bOEQbJtaxL3AAbAyOOu7eq2ac51HOUU4Te0/eIv/uLu/ve/f/eABzyge+ADH7jyqDzoQQ/qHvzgB3cPechDui//8i9fTUd72MMe1j384Q/vHvGIR3SPfOQjV4NWj3rUo7pHP/rR3WMe85juK7/yK1f8Hv/4x3dPeMITuq/+6q/uvuZrvmY1Pe+JT3xi96QnPal78pOf3H3d133dqj59/dd/ffe0pz2te8YznrESQXTsn/nMZ3bf9E3f1D3nOc/p8D79v//3/7rnPe953fOf//zVwN8LXvCC7lu+5Vu6b/u2b+u+/du/fXVPeNGLXtS9+MUv7r7zO79zdV1+13d9V/fSl760+57v+Z5VXf2+7/u+7hWveEX3Az/wA90rX/nKlbj5wR/8we6HfuiHuh/+4R9ePSv/oz/6o92P//iPd69+9atXz+v+1E/91KqT/nM/93Pdz//8z3dveMMbuje/+c2rzvtv/uZvdu94xzu6d77znd3v//7vrwZCYPze9763++u//uvuQx/6UPexj32s+8QnPtH9x3/8R/fZz352bVFzXp4DpPNMPSRkYJA+SXnvXRvRDjuwAfYMQnKdc5/lt+MG7sl4ComDuIhzmwFa8kcbw/VHmdO3CIfYwG+Ivv7Kl/RBYIVgZbupT0JbRt8Ju5heec0116y+T+lxIx/YBg/u9xmwz+uosOUkrMOlhm0WM6IMDcsicNDCjwrLDZELcwkBwXbSxpn80QAy4kWDR8PDSBQvgC9fAk9jTCeXY2ggGXXMQ978Tsd3LPFFWtwgaCBpGMlf2YCTDjYgkDiulQaGBj8dsf6Nbgn18BBtzNSWQxDrXIPpoLCQVK7JdGZ2nXFQS/3JwBId74c+9KE7z6SoJV+xI4MVaWvcfn7l7n1woI4hdr/3e793Vc/e/e53dx/84AdX91Ps4X7M/ZDj+H+Oex31I+khvHKfT/05aku7gDBj0IT7NOeTh/y26fk1hBhCESGWez99DgZ6uS4JeOTo75TPwrEPu/mtLMd1s5dyPAKMvku8fNgG820He8gXn20CZYcnty/uOJ++Dnaz3Ta+bdLcxzG5J07tmd5H3lpP86CFXyruUjpzNIpDDRyNG52ydNDYZjSMRigNKVsaPEZqaATZhwjhk/NpkGhsEV005HT8EgdbGnj203CXgUaMONKY0cByA6ADOXRDIX4afNhjL8fnVQ2JF1u5GdCwxIbsW/oW/rnhLz0vh2B/yot62HLgWqQNoKNEByttB3nmGs3IdQsM6PTRQaN9oa2izWwpkC/aGMqxDJTp//7v/648Up/5zGe6//mf/1m125/+9KdXnqpPfepT3b//+7+v2uV/+7d/W3mwPv7xj3f/+q//umrj//mf/3nl2froRz/a/eM//uOqXnz4wx9eebwQMx/4wAdW94+//du/XXnC/uqv/qp7//vf3/3FX/xF9+d//ucrD9l73vOe1YAj954//dM/7f7kT/6k++M//uPVfeUP//APVx613/3d3+1uv/32lYftd37nd7rf/u3f7t7+9rd3b33rW7vf+q3f6n7jN+z0hW4AACAASURBVH6je8tb3rIqt1/7tV/rfvVXf7X7lV/5ldU0Pjx1v/RLv9T9wi/8wspzBwO8eK9//eu7n/3Zn12V98/8zM90P/3TP9295jWvWQkO7kV4APEGcg18//d//0qo4Ul8yUtesrrv4XX85m/+5pVH8tnPfvaqc//Upz61+9qv/dqVAOK6QZREUJXiZOj7s571rNX9mHty+iNzDERzneNFw6b0KagXXBOIQO67/Xt8WYc2tROUKaJn3TOMtKFcd3xIj3TpO2BHbCGtHFeKUq5R/k+/JCIT7v3rFxuxgX1cC/xPID36JaUwI33OJ830YTiWvJAeXkc+6RutIhr4E5vh1x8gzz7yzfelB/JH/YGvYVkEDlr4ZcrdEi7CNIw0WDRcZaDxZNSNRo6LkU4q00i4+fQbGRpKGqW+GCNOzqeRo7Hjf47l5kCHj3g5Jw1+f+pFGmAaT24YpIENNJb9RpC44k1MY8yWfJA2gcaXm29G5XJcme8lfyd/WegleV5yflq3PUtYt1xWubZpN+hYlR0gyjedLq7TpYe0p7R3tP9l27P0vMX+3N9arrPJa+1bpn8ibF/3utd13/Ed39Hd7373677oi75oNcUV27nWIhbnKK943MopzlzfeOGoN/zev2+HMfdv9mEv9/1+fwT7ub8PefySLuK4HFiib8G1WAo/4qb/QD8APpyLgGObQJtFX4TjOL4MpE86/XzEhjIt+j6ImIhB8oR9fc/iUDpJM/HCsLSR/elfcT7ikT5VCyGDGS3k5ZDyoPC7z33OiY2aC35TY0oDh8DLlAkaURqaTNeiQSeks5OGtMwvnR8afBo7Glka0XSKOI70+aShLRvTpEWDR8OMPaRF40Yjl8aUeIbO53c6XpyXziZ5oLNdjtSV9rbwPSO88DHUTSBlxfXRYjiqY5K2o7zul8yBcqRzS5tDJ432J23PkvNV2p5OWWv5KvO4tO+Ipgg87rcJmVHAPW+OQL0vRVHuy+kbcH3ghUzfITZRl+hXIGD6g78cQztCW0F8/ZA2hHqZdDgmceI5wi5C+g6wwhbio0/StyfHlQKW8+mrcF/t/x7RWoqz0q549EiTto52gbRJl7iwfSjfiSP2rjJR/Ik9nD80eF8cuqivDi4tqrjOGavwW4jwo0EshVhKkEaTRjTTGWjYEhjFKhs4OjjcWNK45rjEQfw0rjR+CK6MSrFFiHFcRrXKhjuNKeKTBpNGkJDGsBzhogGlQS4bcOKkAaFzzY2DgI3YXh5HfIl7ddDC/8BTr98yCjHCj22LgYEfrt/+DIHkleuajmJ/VD37a9zSbtFJI09lO5L2ihF/jmEfHbyWAu0knUxmXRjqIEC9i+hDVNH+E9hSVnzmuL+RHvdv0st9nHQZOM71zTXBPbm8brCV+zLtQDmYW9LlOkIgld607M+5Q20M6XMt5v6ffkaOxQ7aoDALN/o42FMOEA/lj3ixjfgSZ+xKexAeMKDd4PeE8ph+3oibcyjb/j7OZz92Ipb7QjTxL3Ub4beEWXNLZTyF3Qct/BBLXOxlYzIF5F3jpOFglKgUUIkzUyT6DQqNKMdndCqND8f1G/OMRtGY0zhzbgLnsZ90CDS+NJyJg/3pFCIccxzHRtDlZkLjTsNYeg1iZ3kjGfqN+Gh8y8Z4ZdDC/0RQkH/D/gjQEaNer2sLUk4tCr9cl+n49BlEFA61P/srsaNTpo2ijeeT9oqzaI/SDtK2sQ8GLQXyRHnO5UFqid0UeaGeRfTh3SuvsbQt/D5HSJ+h9E5R/+kr5F5N/WG6JvfwhIgfBAw2c+8vw1C82R+P4ro2JsdlG5FIn4LvpBdxATu+s4/rGJtLD2Paq/SJsJt+BwKRPPX7EDme/VwvxNc/Jn0k7EfElSFcOB9b+4H4L7/88pXw6/eRciz8l9gGpe6yNSyHwEELPy5iPrUHGhYarDTKpb00NHRs+s/k0FDRiCHKCPxPHHz4nkBjw7k0Wv0GjYadUTI+fOfYUrjxG40anrmhRo+bBzZwDMdGIGaqA401aXMDKn8jPYRQ6a3kpoLdxNNayLLe8DLshwDCj7aAuti/6WMRdZj9LXakqXe0If2R8JQE1x3tTDkglH01b+kgUpZlhyqdNNox8kPeaW9aC5k6mPa/tfwtKT/UsfQ1hsRdBGF5X54yf6TD/TXCiLS4r3IPjgeQ651rpBzIpS5ha3lfjp3cyzkXUVgO7GY/1xjpDfUTcky2uUZhhg3YRd+H6zm2Yz9xloIv53NsvGvYSrp4M8lLv/+QPg1pYRvteyl2E2fsH2ojw2Uo38QPS+xIfsr2iPjJL/eX/u9Ju+YteSdfeFwNyyFQv+qZkCUVlk/tgYaDRrXf8aLRY5RuaBSJRo6GiP3p3BBPRtDIM8ewj0an9A6yj7g5ngYvjWXSo9HlBag0sAg34oxnsWTJuexDUBIXjQMNIA05eeE3Roo4hgYeO4iThpdzcyz/8xlq5Mv0lvo9HQM634b9EKBTsUn80eGgrWBqS0sh1zR5S6evpfz180JHJTMWaFNog7btcNE+cf62x/fTnvP/iIkl2Donl7nTStvO9TUk+qhT7KPtmStgE/ca7uOl2OR64H6OTbQFZXtAX4H79FA7Qf+A6wLhRL3jexm419O/4Nx+muVx+R77OJ4+DDbRHyDEjqH+Bvup7/QlOJctaaf/kviz5Xf6G7lWNonSiMn+rIcI0U1csL+0qbwmue+knxW7lrRt9b64pDI4ia31q56T5GrLc+Jp4eKrKdAgcUHRuNFQMmLFh0aQxhghRUNIp41GpGxIynwwksRxbImT4xFaxIWoojHLuaSHN5DfadzYx3n9wG807DRWaVSJY0iUJR/YkGMQfzS05IsbDcew5feyU0WZcANgpI7zayujPpdd/09dJM+G/RGIp4QbeVkW1D9+49NS4NrjmqdN4Do7aeD65/y0CyeNZ+i8DGLR9pXtDGWyrl2gXeFTBtogOmC0o+QbW+nkloFjKHfaXtpZ2jgC6fQ7w+V5NX2njaae+nzffkuFe1rajLItKa1Ke9MXS+UxY3+n3iNy+iKM6wU7aA/YF5vpGyD68KINCTuue9oP8spxXK8JXK/0JZjqOJRmjsuW65GB4YhMBGoGijkm13DfdvZhP2lFyPVnMCWNHAsHBHeOP0pMkn/iTyBvnJMyLtPDFuLnePhF+A4dA/N17VjSqnVL3sh/awOitfIey662ejHHpJIHU6m8NQUaP6YcYF8+V1111UoEIdxoKIZEWU150JbjEeAmSwOq1+943KY4Op0xyiOdH9LJQjx0PloJdEzWTc/aNo+II5jxYXSbOEuB1o+H9hbxheeNzlFfoA0dT0ePc2j3IgSxm09fEHIMxyLyyrKig0oHlPaT75RtmTZikA5nBpsyABZ7yFe8heV52V/LlvxRdykPw34IrGtD+tbQ3lNWc97PqR8ZfO33fajXCC3sog/CtcA1Sr+D3/iUbSJ9FcQM1xv5KD1itAG0B3xIE3E4JNjCJHGxjYesL7Y4lusQEcm2vA65prE9tvC93J90+C15SjqIXa7/oRCxWXoEyRt5gg95Iu+0Qwg49sGE9Dk3XkqOiU0cx/lHtZVD9tT0m8KvptLY3haF30JW9dy+SD1yqQTi9ePGZ9gvgQhxbtZ0Dgh0hPi/7Pjs18rdUk+HpuyQnCRGOjAwovNKR4BOYjqU/AYvtnS4OJbOIx26sI04ozOEiOyLxngUSIPzGWUnTtKgg02HjN8JxEEHjI5t2TEkbTpddPDwxHB+0klHkH3Ew//YQSc2I/T8z3VJnHT0yrhPwmzKc+ALW9uRKSkPx039i5hgoGhTGXAs5cRnzkDdp44MPa+Gvdjft5v/OT7iB9uJh2sRwZQ4EXc333xzd+bMmZUAQ/RxnUX85Pwyv8TFdcd1SzvB/3yHy5Ag47rnGsQe2gbaD67HnB9bhkQm1zFiDbvIUzyZtFlc90OhtAdRyWMu5Js0sTdeP9ocbIIB7RJpEch/jmEf7UraobRBQ+ku4TfKgnLS47eE0rrHxnlbnHvSreJbrR6/KuBoxOwEuPnRiCIwDPsnQAeC8uAT0cH3VjwpdFroyAx1xo6iTyeJThedHzoydLroINERYDSdTg/HILboEFG3+Z0OEOmmc8a5/M++CBZ+S6cJO3IsnTSO4/iEdEiTfv4nX+VxfOc3OlzpsJVx9DtipEN6xEcgX3wIiMGhTvNq557/pCPmNM/5C4L6T72gjUD0lfVvyJqU1dyd5ogwBl+oy1yn2E6dRyxl8KO0uRwk4jw+5TWGgEFMca3z4R6GICzFFPHT1nCd056SNtu+UCJdjqUOEye29UMGg2BNeoi5tBkczzVepkX7wPXM4yylXZQR13gGn/rp5H/2w4W2H5FYli3fiZcyZ+ootvdthjnnMuOAbWlD0ljidl91eImsarL5oIUfFyANBxe0QQL7JsDNgvrI56gb0b5tPZT0ualniudXfdVXrcqGjkb/xr5EHty06ejREUNcHSdwPJ1c2k46cBFedHrScUxnMaPcHJcOU8QcIo9OGufxGwKsP1LPeXQ06bhhcxnoQHEO6ZMe2yEhm45i2VklHjqL2JA0I2hJK/mgrEmHzhsBe07CrLR7qu+5p9E5NcxLIAPJ24g+LKO+09ZTZnMG6jjXWwZauF74cD1znZRirbSL64vz4i07bhuYa4vHVhB1j33sY1fed66rdWmW6fe/x5O47l7J7zCmfSFfGYzqx+P/Jyewrzp8cos9EwIHLfy4OdLwepP0YqiFgB23WkriHjtK8feABzxg1Wa0MFgU7xjCCWGzTaCzh/hCQEVE0Rlk+iRxwKX0kiGoEH7sj3AiHTpjiDn20ylLJw7BxSfHkh5iDrHNOf0OYjqw7OMczsWL0O8MItawg7TKUJ7POaQFDzo0SYv8kjf+j1Dse/zYl+PL+Of8jv0OHM1J/Py0EH54fLYVRPvuf1CXqf9cO9Tvbe0+P9f+d8gE9l2HD5n9Lnk/aOFHB4UbpVPrdqlCnjsmATqZ1ElGYw8x/N///V/33//9392nPvWp7hOf+MSqA/9P//RP3Yc+9KHu7/7u77r3v//93Xve855zU+/gtc3nz/7sz7q//Mu/7P7+7/++++hHP9p9/OMfX6Xxmc98ZivMdIro1GWUvIXygRvCK96ubUBwDoMT5D8LFSCMEUzEg/CDFSKIDiWCjWMRUgmIPKZ8IvxoeyPyEGWIM/ZxDCE2RmQmDrakE6FGx5U0KKO+KE8c/XzGDrwd7GO6FueWopE0uE8QByE29qejkgeO2af4gxFtx9wepLJM/L49Aeo+5ZWBku3P9EgJ1EHAOlxHORzXioMWftyoaXjnnmN/3ELy+MMi0OoiLx/72Me6d7/73auH43/sx36se/GLX9w985nP7B7zmMd097vf/VbXItdjrR/EAQFxgY1LnykQQTTkCRu64hB4CBwEX1/MIbrwgiGI8K5FkMGJ7wioBEQaXrV+HAhFRFiEGyIKxgjEfhzERZrYzjEcO+TVI13iI62+8IsdfREXO4mTTjnHEfi/fGYxx8EFQYgHZV8h9zKmGZas92WP6R5NIFNDKTuDBJZIgHaVNt46vKzSO2jhxw0yncxlFZvWtkyAjir1cqkj93/zN3/T/fqv//pqeh/C7pGPfOS56yzX26Yt0ym/9Eu/dCUCOPcrvuIrusc//vErwfXkJz+5e9rTnnbuNSd0nrb5POMZz+ie9KQndU94whO6Rz/60d3DH/7w7sEPfnB3//vff2vbvuRLvuSceIj9dPqXGvBsMS0Sr1zE1rq8IGoQN+S3/4wbvzGVkzj4zoeOAB6ooWmkHIfAKxduoC1G3JVTKBFdjCjzLBCisAzsi9CM4MKuMj2EGjZzbvkcH/GQd35HOA5NDeXcTIOL95F0iKdMg984bp8dH9ilA3ZUOZYM/b5fAgq//fI39d0I0M5zH2SwybAsAgct/CiqLNxQTu9ZVhFqbWsE0qDSmas5fPrTn1558F73utd1L3rRi1aiKoJoaPuwhz1sJdpe8pKXrLw0b37zm1felA9+8IPdf/7nf1abVTrWiAzyRGeNgNDhf35fckAoIPyGxE/yRX3Eu8aWEOGGCLr11ltX5yJ+EEsJmRLZ99RlemXf+xZvGnUeQUZ6fPCwIbSIj0A7jcCDO8Ixoox92MX5TBV9+9vfvjqO8zgGOxCbnItojFjj977HkzSIi/Qp+4TYThrYxbL1nL9P0YdtsKQu4ok2LIeAwm85ZaWl9yaQeyBbw7IIHLzwS+NLZ8AggVoI1DYgQWf4ne98Z/cTP/ET3fOf//zuEY94xKqzOSTwHvWoR3UveMELup/8yZ/sfu/3fq/7h3/4h1qwHtuOUvTRsY4QYJspuUue8hkxk+mUpZAij7SLiKCIPgByDOKMtpObfrkvgJkSyjF9UYTAY+ok8YZlziEe4sPLhzDDm4awYsEWRBhCj+fwiHcoTeziPJZ27x9Txk1+MtBHGnlmEQGFlxIb8txhbMs28XDOUB5y3FxbBDvXIO1F8jRX2qazG4H0PfrXyG6xerYE5iHAABhtz1BbPI8FpnJSAgcv/Oi0UXmX3Hk7aeF7Xr0E8tA0Hd65Awup/PIv/3L30pe+dOVteeADH7hW5D3xiU9cHfeLv/iLq0VXPvvZz85t7mTpIUzi6StFXxKkwxbhu+SbH+IHsZSFWPDk8UFwIaT6Ai35n3qL+MIGxN/UNhA/wrH0Wk6dv13jp22I6Fty/duVw1LPTxvvoPNSS/Bw7U6/2VkGy6wDBy/80nlj9G2pgU7L1B2jGtgcSj5hjVeCTt3Uz/mxyiXP49G5fvazn9096EEPOidmImrY8nzb05/+9O6Vr3zl6vgPfOADNVSJyWygrm0SfUkY7xB8EE1L73zjMaI95FOD9wjRyTRMrgXD+QQi+qh7+xgcOt8a/zsJgXSeHXQ+CT3P2ReBsu3hXmFYHoGDF3508NLBXV7xfd5ipjXx7MqSRquPy3rd1LHjxrOU4xER1EumU+wSeCUCr0Fget1b3vKW7vWvf333ile8omORlNT7/vYhD3nIaqomz+79wR/8QRUiYBcGxz13W9GXeBHnMGxB/CVP+95SBgxGlAup7NumWtIvO16KvlpK5fh2zDW4d3zLPEMCwwTiKOF+54DcMKMl/Hrwwo9CyrM6tY7Y0wniBs+0J4QAHaLyWRxE36bFGchjnq3hXDqqiEWmeK0LpMkUlDxrs+64od/xFmBvFlYYOqb/G+dgEwszYCPnlkIW4UIes8hD//wW/48goywIvN+Ocv+Xf/mX7sMf/nDH6pnvfe97V8/escjGj/zIj3Tf+q3fuuosP/ShD10r7BIvWxZced7znte9+tWv7t761reu4m2R5bZ5gvU2nr5+fJm2xfWZ8uof4//bE8g0T6acbmqnto+xjSNpG3P9KvqWXabpRC95ttGyS0Drj0OA9iZtz9QzkY5jl8cen4DCr+tWQogKXeOUCzpAdH7oWMatTuc/wo8tYohFD3hOZ0i8cgwLI7CfThQdU1alQ2glcB7iimP5Tpos+AAXhGUCYqwUZPyOjdgWcckoPefRCU7nhH2k2z+X80mX9BCZiYOyIN4E4sdexB8jTUPx5NhWtnn4n1cZpME96Zay+MZv/Mbuu7/7u1ccYc5L0g33EDip6CMGzuV5B8oH4VjDVMl7cra8b7QbQ69xWF5OxrM4C7lQx9Kujhe7Mc1NgDaCsmSmgEECtRLg3la2PTX2k2tlV6tdCr+uW3m20mGrqaAQYKwyhxcsQo8bPiKJjjsBccQHQcfvjAhzoSZwHr9x4ZYj54jFeM8QUQhBVrVDfCGw2Md3BAPCL7bAibjSsWXL/xFunIs9bOm4YS+2IgYRkqRT2odXMYtIxGbSIv3Yy/EIP37HpquuumpVZjm+1W2mEMIR7rzf7su+7MtW78XjNwTGU5/61O45z3nO6nUKPH/3mte8pnvDG97QveMd7+jOnj278g62ymfMfFHHTuLpK20gjog/p32WZI7/nTaH8nDhi88PKpQdL0Xf8etTrWekvbCe11pCh20XfS76gPQ/WDnYtqeN+qDw+0I5UrH5RNDsu3jpRCLYyvddRaAhBrkg+R9BVAq/UpSxHwHG8RyXgBDDaxZhFfFGWngGIyq5yBEYeBP5jhhD4JU2ES//R7xhF4GOGw1Gzo3AK+3DbjyZpEl+CQhVbIso5TeO40NgHx3CQ2iAGFmjTjrCtir6yf6MIfpiHHHFU4v4s0MXMsfb0jaljTvemW0dTducAQk6Xmlf28rl4eYmbTyLRBkkUBMB+nDpFzNAYdtTU+nsZovC7wv88oxOLYIC4cPFxo0BAUdA/JTLm/M/wov9dDAvv/zyldBLlchvZRxcvAjBUoDFI4dQK/OPaOQ3PE+kFYGI+Ito5HhW3sOjl05uRCvnkjbnRSDi8cNePjQs5KcUpXj0iCt2cBz7SR9RiPeTc/ifwH7ij0c0eW9hC08aXp8BmbY0I9S43jIAsWuK8dZSfqnLu8bp+YdFgHaPwQPqkB2vNsue+zHly73SIIEaCNDuUB+pl3zowxnaIqDw+0J50jmjkiMAawgIIDxp8XwhbhgVxAPHvlIQ8R1BVU6N4jeEFXHEg5dpn6VIi5gi7+WU0og8GoB0XCMQiZeQY+icIBJJk4AoQ5yRTtImDoQpthNoXLCtnJqK0CEP5W+cnziyP+IxaUUEriJu6I+dgnkKE+E3puiL1dwwc/NkoGUsUZn43bZLgMWaUne4J1l32i3rLC5nB7vdMl5CzmhjuE+l3XGwaQmldjIbFX5f4EalT4VH0Ow7MKWRDiliB+8aWwQR3jq+I4YQT4S+Fw8BhtDCExfP3rrfEGKIu1I0EifxI9xKEUac2BQhyLkXXXTRyiZsSOB30i7FYEQoNsMa8Ua6EYKcj5ckwjb5Iq8pD+LDzohhuBBfi96+sEydzP9ul0WAa4UpepQjdbe8TpaVE62dg0C/86UYmIP6ftPgHkb7wABq7nX7tcjUD40A/azMLqAu2u60XQMUfkX5ZnpWDZWemwEeMQQUnj6EGCKMaZZ0Jtnf97AhCs+cObMSXIikTOmks8k5iEmEIN44/ucY4u573kDCsfxOg5DAOYhBRBy/s0ooo0Icm0DHJQK17OSSFuci9BBusI6IxGPH7zzrxzGIQo4h3n4c8CB99vFp/Uap8EvNWu6WOsx1krLEm2OQQJ8A9SRTrBgsKNve/rH+3xaBPGrC4BD3UIME5iBA/4l+Xu5N9Mla71PNwbX2NBR+RQll5I2bbw2BG0BuAogjbg4IpjxfV9rIfsQV4owLF1GIMMIjh1jid37jOMQeIouOBt4yRGUpsIiX4/g96fMbxxIngi/nsC0bCtLgN1iWAZsR1KRN3KSHMGWlz+SJ+BGAxI/wK9NO+vxOPkvhW6bT2vcIhn75tJbPQ8gPdT83WOp+ed0cQv7N43oCDGalbnDNWzfWs2pxD/e6tPWKvxZLuK48Ud/KaZ2u2FlX+UxtjcKvRzjz7WsZbUVIIXLoKOLtUwD0CqzxfxmBo0PYF9KNZ7vZ7NGuZOonU2v0/jVb1FtlDIFXjrjXMNtkK8M9aHQCir/RkRphjwB1jEGmclonA+/8bjgcAgq/Xlnj0aKjzc143wEvGR4wbOLiZGs4LAIKv/bKm5tspnbR1vjsX3tlvE2OGARIB4zBAAd3tqHW9jF98afnt+3ynjN3PAaU9ob7Dvcg69ecJVBPWgq/XlnQ8GZEfp83Yjx7TM/Mlufm9Pb1CusA/sUDQCOtJ6C9wqZ9yQwDyrhcSKm93JqjEOAewwwOyjwdMH4zSAACpfijo+5933qxCwEEX54dpr1hMHmffdtd8uK54xBQ+A1wTGebkfh9BUaD+fA8HM+8IQINh0cgdVHh12bZ08krn/2jo8dUHIVAm+Xd9/I5i6PNct41V1z/me1Bm+D9f1eih3U+9ac/pZNBRtofgwQUfmvqQEbi99Xgki4P3/JcH50DnvUzHB4Bhd9hlDlTbtLRY1SWzh4jtYY2CPSf5aOsnWbVRtlOmQse8YhnmI68QQKbCNCmMHOE+0fqDW3Nvvqxm2x13/4IKPzWsOdC4cLZ1wqfrHCJO35oBc81JvtzgwQUfg0W6oYscc2XApD2RwG4AdgCdrGATzpirp63gAKrzMT0ReiP+DxwZYVTiTncN8pVOqkr3Eec0llJAVVmhsJvQ4HE6+c0uw2Q3DUpAYXfpHirjZwpOWl/MgDlFNBqi2vQMJ7NKlfsZDEFp/AOovLHIwjQgS/bA71/RwA7gN20JQwqlc/vca/AS+xsggOoADtkUeG3AR6NLRcSHx+w3gDKXZMRUPhNhnYRETPan/d70Q7hOaLT54297uKjjHLvoMPuszV1l9cSrKOjXz4P7GyAJZTa+Dbefvvt9/Lu0cbQV/C+MD7vFmNU+B1Rqmloa3i9wxGmurtBAgq/Bgv1BFliEKqcAoqoYGqPU3lOAHPCUygPpuNF9HH/0Ms3IfADjJpB6HIwSAHYfiU4e/bs6tm9vnePWQQOKrVf/mPnUOF3BFFu2pliQSfcIIE5CeR9bzbuc1KvNy2ERbngAwIjHT8Fxv7KDfYsqhDBxz1DUb6/8jiElJkNkL5J2Q4cQt5bzyPinmmcvPYlzweXbYvevdZrwLT5U/htwZcbeC46b+ZbAPOQ0QjEy2O9Gw1pExExpYebf9nxo4OAF9Bp6fMWcbl4C/cJBwjn5X/oqSkAl10DGDTCo8f08CGhR5tCO8/sAdv2ZZd1LdYr/LYsCW7mXICMrjuyviU0D9uZQKaN2eDvjLLZCOj4ZYAgA1TUGwSJbdV0xY4Xvpx6RRl4nU7H25g3E1AAbuZTy15EHm0zg3Rl+5G2O0KPmR2Uqc/t1VJy7dih8DtGWWZePaMyBgnMQSA3gznSMo1lE6CDwKgwrwxIvWFLB4MFAQzjEMD7Xq7WOM0NJQAAIABJREFU6bTOcbgayzgEhgSgC0KNw/a4sTAQFJGXQdyybc53Bo1ouxlMUugdl7LHH5eAwu8YxLgg06lyOs8xwHnoiQhQ37gxUOcMEjgOAToQeT40nQtGl3kOTa/UcUjecyzXIyI6PH0n3z1s/FYfgb4ApN4iPngvqDMBpimvCL1yYCjtRbYReZSPbfE05WCsmwko/Dbzudfe8nk/F9y4Fx5/GJEA9YubBR14gwROQgCxcsstt5z3LCB1ChHISDT7DZsJ0EkuX8+A4GPgz87zZm7urYMAfRamDWbQOgKEmUuIQMNuBGhDaR+Gpm0ySwz2tMGKvN04e/Z4BBR+J2DJRUzjyWIKXswnAOgpWxHIc6V6l7fC5UFHEKCtGpoK6vOA68EhjstV9ejEKZbX83JP3QTwMvVnAmRRKBcQO17Z0Q6UMwDoEzLtmzaCQVsHho7H06PnI6DwOyFrLu6MnHuBnxCip20kkAU7vCFvxOTOExCgY0Ibts4LcOhtGp6QcgTfhVtOUMk8pVoCXN8MYGfdgngBEYF4AhnwcFB7uPiGBB9tqffpYV7+Wh8Bhd8Jy4SGM40mI+aH3lE6IUZPW0OA+pSbsXVrDSR/HoXAkBeAupepYIdS/8gnU7ZKD58Lt4xSxYykYgIIGWYCUNdzz8k2QpDrgtUoDzkg7GgTw4Ytgs8ZAIdcK5aZd4XfDuVGRyHijwbBIIGxCOT5PuqXQQJzEKA9WycCWayg1WcC6bj1BR8ePlgYJHBIBLgWqPcImiEhiNihLeB6wSuOGGx5YAivJ3ktV+RkloSC75CuivbyqvDbsUxpGDJdivneBgmMQYAbCzdZpuMYJDA3gU0iMM8ELn0qGB3X/gg+gs8pW3PXNtOrlQBCkEFIPIIZ5C49XuV32gVEISsHI5Z4hcwSvIS0ddjJB7uxn3yUnn/yST/PRZ1qranadRwCCr/j0FpzbCn+aCANEtiVQG46TiPZlaTn70pgkwjkObglvSeQthp7c32l48pAy9KF7K7l7PkSOIoAbQFCEAHEIjEMlGTgO9fSpm3EIcKKDwMviK1Nn4iybbab4iGtpDsk7IbsxutJn448GyTQCgGF30glyfSINBxOERoJ6oFGk7rkNM8DrQCVZ5tO0LqpYHSualsYAs8Do/jlYi201VxfXGt0Zg0SkMBuBBg4wVuOKOSDKOSTflHN29iKyGOWDfmwXditPnh2vQQUfiOWTTrsNHCKvxHBHlhUjEZahw6s0BeaXTp7dPKGpoFlYQimVM7puSatoWmcXFN4J+jczWnPQotWsyUwOoGIQ4QVn3gPIxaHthFl22yHzs9vpJV02doGjF68RrgQAgq/kQtK8Tcy0AOLjhtjOqiOOB5Y4S88u3SkaP/WeQMRggxqMB0LL9wYHS/iIC7iXDd9C1FK549ryyABCUhAAhI4ZAIKvwlKX/E3AdQDiTKLuvis6IEUeMPZLIXgpueAEGwRhOUzOgi6iLr8noUXOJ4BkqEPz+Xw/BHt8BjisuEiMmsSkEBB4Bu+4Ru6Zz3rWcUvfpVAewQUfhOVqeJvIrANR0snNR1ZO6wNF/SBZo06naldTNvaJAZzHRy1JQ7iwqNH3HrJD7RymW0JjEAg7c0IURmFBKoloPCbsGjw2qQhQQgaJLCJQLwYevs2UXJfawTy3E2exckWrx2f/J9tjm+Ng/mRgAT2SyD9tf1aYeoSmJaAwm9avqvnXdKYKP4mhr3g6OnMUk/wYOi1WHBBaroEJCABCSySQPpqizReoyWwJQGF35agdjksz23RqCj+diHZ5rkIvSw17wvb2yxjcyUBCUhAAnUTUPjVXT5aNw4Bhd84HI+MhQ59GhXF35G4DuoAXihN3fC9fQdV7GZWAhKQgAQqIpA+WkUmaYoERieg8Bsd6foIEXxpWBR/6zkd0p7UCaZ4utz8IZW8eZWABCQggZoIpH9Wk03aIoGxCSj8xiZ6RHzp6NPA3HrrrUcc7e6WCZR1wYGAlkvavElAAhKQQO0EFH61l5D2jUFA4TcGxWPGUXb4meZnODwCWcyFG43P9R1e+ZtjCUhAAhKoi4DCr67y0JppCCj8puF6ZKyKvyMRNXtAWfYs/GOQgAQkIAEJSGC/BBR+++Vv6vMQUPjNw3kwFbw+eYnx1Vdf7TL+g5Ta+lHR11Z5mhsJSEACEmiDgMKvjXI0F5sJKPw285l8Lwt6RPxddtllir/Jie8vgdOnT7u4z/7wm7IEJCABCUhgLQGF31o07miIgMKvgsJE/F188cUrUYD4u/vuuyuwShPGIsB7+vDo5qaC188gAQlIQAISkEA9BHKPrsciLZHA+AQUfuMzPVGMiAPe40bDc+GFF7q0/4ko1ncSoh4xT7n6yob6ykeLJCABCUhAAhBQ+FkPDoGAwq+iUkb8XXHFFefEn56higrnBKZQfoh4biaIej25J4DoKRKQgAQkIIEZCCj8ZoBsEnsnoPDbexHc2wBWekwD5Lv+7s2n9l8Q8LymI2VIefKbQQISkIAEJCCBOgnknl2ndVolgXEIKPzG4Th6LLzbLY0QIkLhMDriSSLsT+3UazsJZiOVgAQkIAEJjEogfa5RIzUyCVRGQOFXWYGU5iAayhU/ERWGegngnS2ndlpe9ZaVlklAAhKQgARKAgq/kobfWyWg8Ku8ZBEP5aIv73rXuyq3+PDM49m9K6+88pyH1qmdh1cHzLEEJCABCSybgMJv2eWn9dsRUPhtx2mvRzHNs3zuj/fBGeogUHr58M4qzOsoF62QgAQkIAEJHIeAwu84tDx2qQQUfgsqufK5P14R4FTC/RVe38t36tQpn8PcX3GYsgQkIAEJSGAnAgq/nfB58kIIKPwWUlAxE7GXl73TSN10003Z5XYGAnhfYZ4bhF6+GaCbhAQkIAEJSGBiArmvT5yM0UtgrwQUfnvFf7LEER/XX3/9OfGh9+9kHI971pkzZ84t3sINwmf5jkvQ4yUgAQlIQAJ1ElD41VkuWjUuAYXfuDxnje3OO+88z/vnax+mwQ9nxHVuCldccYXTbKdBbawSkIAEJCCBvRDIPX4viZuoBGYioPCbCfRUyfS9f7xOwJe+j0P79ttvP2+1TqbYunjLOGyNRQISkIAEJFATAYVfTaWhLVMRUPhNRXbmeFlsBE9UGq5LLrlkJQARhobjEWBKJ/zCkuf4brzxxuNF4tESkIAEJCABCSyGQO75izFYQyVwAgIKvxNAq/mU/vRPPIC8/gFhaFhPAD4s2lIKPjx8t912m6t1rsfmHglIQAISkEATBBR+TRSjmTiCgMLvCEBL3c2UxNIDSIN29dVXdwhDwz0E8O6VL1+HUwTfPUf5TQISkIAEJCCBlgko/FouXfMWAgq/kGh0iyerfPk7DRteLQTPoYazZ892LISDNzQNPVs4KYwPtVaYbwlIQAISOGQC6Q8cMgPz3j4BhV/7ZbzKIc/68Zxa+Q7ATANlEZOWnwUkbwhdPJ59sXfppZc6nfNArgGzKQEJSEACElhHQOG3joy/t0RA4ddSaW6ZF55b608DpcFjyiPPueERW3JA6JEH8lK+hiGNOmKP9yD63OOSS1nbJSABCUhAAuMRSB9hvBiNSQL1EVD41Vcms1l01113rbyAQyIQzxgeMl4NwXE1B+zDo8f0zSGhR2N+6tSp7pZbblHs1VyQ2iYBCUhAAhLYEwGF357Am+ysBBR+s+KuNzG8ZCwIgycMj1gawHKLRxAxiCcNoYVXbS6vWbx4pEv62LFO5GEzYpa8kCfONUhAAhKQgAQkIIF1BNLfWbff3yXQAgGFXwulOEEeEHRMCWXBk/K5wDSM/S0iDGHIh9dHIM7KD55DhGL/w/OF5XF8x3OXuMrXK/TTzP/YF4+ei7NMUBmMUgISkIAEJNA4gfQpGs+m2TtwAgq/A68A22YfrxmiCg8ai8QgCPGq8XLzNJZTb0mPdEkfOxR525aex0lAAhKQgAQksIlA+jCbjnGfBJZOQOG39BKsxH5EWD48S4c4Kz9Mu0S49T946srj+I6nMXHNNZW0EoyaIQEJSEACEpDAHggo/PYA3SRnJ6Dwmx25CUpAAhKQgAQkIAEJ1ERA4VdTaWjLVAQUflORNV4JSEACEpCABCQggUUQUPgtopg0ckcCCr8dAXq6BCQgAQlIQAISkMCyCSj8ll1+Wr8dAYXfdpw8SgISkIAEJCABCUigUQIKv0YL1mydR0Dhdx4O/5GABCQgAQlIQAISODQCCr9DK/HDzK/C7zDL3VxLQAISkIAEJCABCXyBgMLPqnAIBBR+h1DK5lECEpCABCQgAQlIYC2B4wo/3m982WWXrT5rI3WHBCojoPCrrEA0RwISkIAEJCABCUhgXgLHFX68l5hz2BoksBQCCr+llJR2SkACEpCABCQgAQkcm8Cll1668szhpVsXjiP8rr322pXou+CCC7q77757XZT+LoHqCCj8qisSDZKABCQgAQlIQAISGItAvHNXX3312ii3FX633HLLOdF31113rY3PHRKokYDCr8ZS0SYJSEACEpCABCQggVEIINDwziHubrzxxsE4txF+t9122yoOjn3Xu941GI8/SqBmAgq/mktH2yQgAQlIQAISkIAEdiaAUIu4GxJt2bcuoVL08d0ggSUSUPgtsdS0WQISkIAEJCABCUjgWATw9iHwLrzwwq4/TXOT8FP0HQuzB1dMQOFXceFomgQkIAEJSEACEpDAeAROnTq1En+8iqFc7OU5z3lO9/SnP/1eCSEQEYoIQxZ1MUhgyQQUfksuPW2XgAQkIAEJSEACEtiaAGKPVT4RcpsWeyFCRd/WWD1wIQQUfgspKM2UgAQkIAEJSEACEtidwDaLvZBKBKKevt2ZG0MdBBR+dZSDVkhAAhKQgAQkIAEJzETgqMVesv/iiy8+b0roTOaZjAQmIaDwmwSrkUpAAhKQgAQkIAEJ1Exg02IveRaQ9/YZJNAKAYVfKyVpPiQgAQlIQAISkIAEjkUgAo/FXsqQ9/7dfffd5c9+l8CiCSj8Fl18Gi8BCUhAAhKQgATqJ3D27Nluis9NN93U7fK54YYbuosuumj1STzXXXfdavEXfs9vbE+fPt1deeWVk33ySoltttiRwDOL69jeeuutg/sUtKF3WFuF32GVt7mVgAQkIAEJSEACkxK48847O8TTJZdcshJQ2wgZj7nPXlhRRohIhC0C0tA2AYVf2+Vr7iQgAQlIQAISkMDkBHhNAt6lIbHHAilXXHHFJB+e0xvrE/GJvXy/5pprzoub5/0QtVN8StGFN46XxrOaKNwy7TT29besPrqO7/XXX98xnbW/P3nsx0X5nTlzZvL6YgL7IaDw2w93U5WABCQgAQlIQAKLJ4Dgw1uUl5wjJBAVCI5SzCwho4ijUggdwnRIyogVTBGZpRhUAC6hxh7fRoXf8Zl5hgQkIAEJSEACEjhoAkOCD+GEiFhqyEIvEa9LzccuduNp7AvAJZfpLixaPFfh12KpmicJSEACEpCABCQwEYG+hw/Bx/THpQemcsbjd+gvbe8LQBa2MSyfgMJv+WVoDiQgAQlIQAISkMDkBHj2q3yGrxXBF3BMe4zwW9o01eRh7G0phnnlxSFMfx2bYU3xKfxqKg1tkYAEJCABCUhAApURwJvHyo8RRSwm0oKHbwgz0z0P3dvX54IIzvRPnuV06mef0HL+V/gtp6y0VAISkIAEJCABCcxGgOf4mOIXwcfqkkwBNBweAepC+Qwkr+swLI+Awm95ZabFEpCABCQgAQlIYFICePQyrRPBxysT6PwbDpsAwj+vl1D8La8uKPyWV2ZaLAEJSEACEpCABCYh0PfyMa3T590mQb3YSKkPir9lFp/Cb5nlptUSkIAEJCABCUhgVAIs3MECHpnaiZfPIIEhAoq/ISr1/6bwq7+MtFACEpCABCQgAQlMSoCpnXkJu16+SVE3E3kp/lj8x6nA9Retwq/+MtJCCUhAAhKQgAQkMBkBntuKl48FPOzAT4a6uYj74q+5DDaWIYVfYwVqdiQgAQlIQAISkMC2BFigI6LPqZ3bUvO4kkAp/lzwpSRT33eFX31lokUSkIAEJCABCUhgcgIRfb6mYXLUzSdQij9e+m6ok4DCr85y0SoJSEACEpCABCQwGYFS9NFpN0hgVwK82D3eY9/3uCvNac5X+E3D1VglIAEJSEACEpBAlQQUfVUWSxNG5XlRFgpyQKG+IlX41VcmWiQBCUhAAhKQgAQmIbBE0fe5z32u+8hHPrISEi48M0m1GDXSa6+9duX5Q/zxihBDPQQUfvWUhZZIQAISkIAEJCCByQjEG8MzfbV7YxB7d9xxR/fc5z63u+9979tdcskl3RVXXNHxqgnEa+32T1aIC4mYsmLaJ++FVKzXU2gKv3rKQkskIAEJSEACEpDAJAQQSkt5/uqTn/xkd/r06ZXgQ/i9733vO8cEQcjiIddff/3KC3huh1+qIoDYQ6RT5ygrQx0EFH51lINWSEACEpCABCQggckI4DFbQicc0ffyl798ZevVV1896NlDxCImXEBksuoySsTlYAMLvxj2T0Dht/8y0AIJSEACEpCABCQwGYFM8bz44osnS2OMiPHmvfa1r+14Ngyhuk7YIShe+MIXro4dI13jmI4A3lkGHChTp3xOx3nbmBV+25LyOAlIQAISkIAEJLBAAvH2rRNStWQJQYeXD6GwztuHrW9729u6xz3ucd1NN91Ui+l7swOxfOedd64+fK8x5Hk/ytSwXwIKv/3yN3UJSEACEpCABCQwGQFWVURIsaBL7QHvEJ4h7F0n6hA3N9xww0aPYO35HMs+WMCJxW9Y8KbWFTTx9FH/KFdEqmF/BBR++2NvyhKQgAQkIAEJSGBSAnmp9qlTpyZNZ4zIETGIg4suuqh74xvfeK8oETqIQ/b3F32518EH8EM8nwg/psjWHG688cZV2bLKp2F/BBR++2NvyhKQgAQkIAEJSGBSAiyCgpii411ziCcPW9c934eIRTggdMgP5xxqKKfFLkUE84wp5Vv7lOOW65TCr+XSNW8SkIAEJCABCRw0gTxftYQpdvH4DQk/XuDOgi4Ih03P/x1CYZdTPJkaixd0CSHeZ2w27IeAwm8/3E1VAhKQgAQkIAEJTE5gScIvz/itm+rJ+/zwFiECDzkg4q+88sqVCEYML4lH3u2n128/NVjhtx/upioBCUhAAhKQgAQmJ7Ak4XfHHXd0T3nKU1aCZt3iLpMDqzyB0ts35Bmt3PyVcMdr67N++ykphd9+uJuqBCQgAQlIQAISmJzAkoQfooZn93iGj5e48zJ3w/kESm9fzSt5nm/1+f/lWb8lTD8+3/Ll/6fwW34ZmgMJSEACEpCABCQwSCCLuyzlObAsWrJuuudgJg/oR1bvRBjjMeOZuSWGrPC5hJVml8h3k80Kv0103CcBCUhAAhKQgAQWTCCdbLZLCLzzjVc58IJ2nmPbl1eId+KdOXNm9Z48XptQg/cxopipkqdPn+5gtW3AfoTizTff3L3pTW/a63OBebck+TDMS0Di8/I2NQlIQAISkIAEJDAbgaykWLN3hSmePN/HawnwZiH6XvWqV3Us5sK+MQNiCSZ8hoQTooQXxONxxA64sd009TRxMvUSscr5Uyy4giDGruN4+7ANby/PA/K55pprVs9R7vsVEFnkhXIwzEdA4Tcfa1OSgAQkIAEJSEACsxLAY4ZnhWf9agx4sViZEsHHMv9MTUV8nSQQF54wxBdb/i8D8RL/y172skFhhghBVCGumFKJaEJ4IuSGvI/swxvIOdiOqGK1SvIz9Pwd6Z8kb5QhcWMXZcmWRXD48B1BR/leddVVK29pBG08hLAlz0kbAcm5+1xZM57oa6+9tiwiv09MQOE3MWCjl4AEJCABCUhAAvsigAhALNQ4rS5CK2IGz9RJPXzEhZh57GMfuxJCxFmKNUQQ4gnhNzRtM7YgpEo7EEvx5JXTTrGT4yK8EFOxnRVJy7Qpe87lN2zEu9kPlFMEW38f/2fF021X8ixFXz/P2IcXE9G6r4B9lBH5McxHQOE3H2tTkoAEJCABCUhAArMTyLS6UrjMbkQvwQitMUQf0yoRN/HSRWTh6eK3o0Rfjsdrx/ERcJiMVwxx0p/qmWmXfaGIqOTYUvgh6PBAktchTyDpRETiXRwSgOzHvm1eXh+xSnp9u5PXfU/1JM8XXHDBikk8kb0q4r8TEFD4TQDVKCUgAQlIQAISkEAtBJhOhwhAPNQQ4o3CJsQZz/MNiZ1tbSVfCKZ48iK+iJ9pmHjZ+l6vxF0e2xdlPGOIQOo/UxfxNGR7hGIZ11HeuvAgPqZAlsITO0sbyWd/f/LCln2IV0RiX9whkJmGilhFuO47LOlVI/tmNVb6Cr+xSBqPBCQgAQlIQAISqJAAYgQRVMMCLwgTphoicrCp9IydBB1iBuHXnz5JGsTPB7GzbrGVeNL64i4eyb5Hr/Te9W1nH/EhtBGHCQgx8rvOW7fOhpwf4bjN9MzYjfAj3oRSXK4TwTl2rm2e81vKirNzcZkyHYXflHSNWwISkIAEJCABCeyZAFPpEECIgX2HeMsiyo77WoK+/TynhrDte8EyFXPdM3XEEzGELbED8YZQQ/AhBom/jDsibJ13rm9fvGwcT7z9MGRD/5jkZZ1wzPGlKM2x2A4fRCN5wgaOqyHUNCBRA485bFD4zUHZNCQgAQlIQAISkMAeCVx88cUr8Vd6ovZhDp19nplDbG27UMmQnQgyBBH5QTyVgamRCDnE1iYvWWzhGOLif8QeduGFGnr2LN67voewTL/8Hg/c0PGIMuJDkK+bfskxTO+EVzl9tEwj3/uilP+Z7knceCH7nDiPY2C5DzGIPakHyYPbaQko/Kbla+wSkIAEJCABCUhg7wQyrW7fy+eXUzA3eeM2AcOLhqAamr4ZMYXYQfitE5eloIpAzOsYEI5DofRWbpo+mnNLD1z/eTuOKeOLhy7nZlseA7tNIVNGk29e74CwHOJEPAhbxC5p7Csg/PgY5iEg6Xk4m4oEJCABCUhAAhLYG4FM96STPeTJmsuwUvj1n5HbxgYEGx4qhF8/sA/xg3cNEUf86zxp8cTBo7/yZT9e/kcMsggN3jnOOWqRFc4p0+h76xCFxIdII75MNS3Tppw4j/18Ngm/LETDcetEZBk3rOLlLH+f+3tW9pw73UNNT+F3qCVvviUgAQlIQAISOCgCWd0TYbCvUAq/k3j8MsVzaGoiQos4EVRMI4xo6gsm9vFCc0QhQukoEYfow3NGvHj6jhJhsMUW4sXTx/F94YfoIi7EaX8hFs6P0MRrR57WeS45FoGIR5djSOsob2REH3la592cq364sudcpD+fjsJvXt6mJgEJSEACEpCABPZCALEUD8u+pvch3HimDoGyzhu3Dg7PozE1cUisxONVeu8QNnjUSiEU0YdQ4tijhFJEFUKNKZMRk5vEImkQP+IvzxGWUz35HQ8f9vF7X9RF9CE04YU4xM6+gIUTxxIP9pEm+d3kSaUO4BWtQfRhv8JvXW2f5neF3zRcjVUCEpCABCQgAQlURwBxgIhAbOwjIJ7iNcOOvidsyCY8VAgohM/Q82qIH0RcKa6IJ4ud4FHDy8kxiCKET+KEQ194cS77EWjYiqjifz6bxFVEFWlxLgERyP8IMuwjv2wRdLEbDtiGyMRmjokwK3n1WSHesS/5yfOA5JfzsbcMOZ48kHYNQeE3byko/OblbWoSkIAEJCABCUhgrwSywieCYR+h7/WLcBmyBeHDtMx1q1JyDsIMMRWxVcZDWpdeemlHnokDz2ACQg2BhOcx4hBxicBieiXpItzKEM8iYo1jbr755u7WW29dCTf+59nCMg3OxS6eO8w5iLuEiFP2IQ6Z2slvpWgjf5mWikhFvGbb99yGbTx/eCYjeLGtH3fs2NdW4TcveYXfvLxNTQISkIAEJCABCeyVAGIBoYHYQfzMHRA1ePAy5RM7ECfYhSeKDwIlggXhs85OvGRMidwkHo/KH+KO83mxeaZXIjhL8VXGETGK3eF46tSplcBbZydxrYuvjHvoO+fBBiFKOtgIn3VpwQS+5IcP3zflZyjNuX5T+M1F+vPpKPzm5W1qEpCABCQgAQlIYO8E0uFm2t++AoKrXGQFERXP12Mf+9jVtEpEzKaAKEIAnVRUbYrbfdMTiCf0qHKe3pLDSEHhdxjlbC4lIAEJSEACEpDAOQKIrgitfXe6EW14pPBq1eqZOgfOL6MSSB0cNVIjW0tA4bcWjTskIAEJSEACEpBAuwTyegeeGTNIYG4CGXzg+UvDPAQUfvNwNhUJSEACEpCABCRQFQGmSO779Q5VAdGYWQnw/CEeP7aGeQgo/ObhbCoSkIAEJCABCUigOgIsakLnm1UiDRKYiwCDDtQ56h6eP8M8BBR+83A2FQlIQAISkIAEJFAlgX2/3qFKKBo1KQHebYjo41UbhvkIKPzmY21KEpCABCQgAQlIoDoC+369Q3VANGgyAnj6IvqYZsz/hvkIKPzmY21KEpCABCQgAQlIoEoCeb0DC74YJDAFAV5kn3cfIvqc4jkF5c1xKvw283GvBCQgAQlIQAISaJ4Ar3RwoZfmi3kvGaRuxcvH9E4GGfb9CpG9gKggUYVfBYWgCRKQgAQkIAEJSGDfBHiZexZ6cQrevkujjfRvuummVZ2iXjGwwGJChv0RUPjtj70pS0ACEpCABCQggaoIsNgGnXSX2K+qWBZnDNM4L7vssnOijynEDibsvxgVfvsvAy2QgAQkIAEJSEACVRDIS7URfyz6YpDAcQnceuut5wQfAwnWo+MSnO54hd90bI1ZAhKQgAQkIAEJLI6AUz4XV2RVGFyu2BmvsV6+KormnBEKv3Mo/CIBCUhAAhKQgAQkAIFM+bzuuusEIoEjCeDVK1fsZAVPQ30EFH71lYkWSUACEpCABCQggb0SYMpnVvn2nkeGAAALrklEQVS87bbb9mqLiddN4PTp0+emdrJip16+estL4Vdv2WiZBCQgAQlIQAIS2BsBvDZM2ePjO9f2VgzVJtxfwIUpwoa6CSj86i4frZOABCQgAQlIQAJ7I8Dqngi/Sy65RE/O3kqhvoRZwCVTOy+++GIHBuorokGLFH6DWPxRAhKQgAQkIAEJSAACed6P5fmdxnfYdYIXr1955ZXnPMEMDFgnllMnFH7LKSstlYAEJCABCUhAArMToGOv+Jsde3UJll4+nv90AZfqiuhIgxR+RyLyAAlIQAISkIAEJHDYBPriz2f+Dqc+UPall+/UqVN6+RZa/Aq/hRacZktAAhKQgAQkIIE5CZTij+e78AAZ2iagl6+t8lX4tVWe5kYCEpCABCQgAQlMRgDxlwVfWPTl6quv1vszGe39RYxHVy/f/vhPlbLCbyqyxisBCUhAAhKQgAQaJcDzXXnPH96/M2fONJrTw8oWwv6mm246t3gLK3b6LF87dUDh105ZmhMJSEACEpCABCQwGwFWeOSF3XnXHx6iO++8c7b0TWhcAuW0TsrUFTvH5VtDbAq/GkpBGyQgAQlIQAISkMBCCdx2220dniEF4DILEG8t72lM+SHmXbxnmWV5lNUKv6MIuV8CEpCABCQgAQlI4EgCN95447npn4gIPIBOAT0S214OYEpnX/Ah3vXY7qU4ZktU4TcbahOSgAQkIAEJSEACbRNAUPQFIM8AXnfddXqRKij6PMNHmcTDh+DDa2ton4DCr/0yNocSkIAEJCABCUhgVgIIDMREXvwekXHZZZetFg85e/bsrPYcemI8j3n69OmuFHxM6XThlsOqGQq/wypvcysBCUhAAhKQgARmJcDzYiwUklVAIwIRIbwOgkVFfKZsmiK5/fbbV4zDnC2Czymd0/CuPVaFX+0lpH0SkIAEJCABCUigEQJ4mBCBfU9ghAnPBfIpFxvJPn5jyqiiZXNlwLvHKxn6DK+99loF9mZ0ze9V+DVfxGZQAhKQgAQkIAEJ1EcAgcJ0UARJuSpohN6mLVNGXTjmnjKFJZ5TuJTc4HrLLbd0TL01SEDhZx2QgAQkIAEJSEACEtg7AcQJ3jw+Q1M/+W1o4RieXRs6fu8ZmtiAdWIP4Xfq1Ck9oxPzX2L0Cr8llpo2S0ACEpCABCQggQMmMLRwDM8MMk0UIYg3kAVkEEetBMQt+WK6a38aZ8QeXPTutVLi4+dD4Tc+U2OUgAQkIAEJSEACEpiBAN5Bnhk8aqpoRCHCEOHEM3D5IBD7nxlMv1cSCLbYgcDDPmzF5nL6Zr6zWA6ePcXevVD6wxoCCr81YPxZAhKQgAQkIAEJSGA5BPDuIQSZDspzg6xeeZQgjIg6zpbn6BBju36GvHab7CAvCD2e2TvEqa3LqYn1Wqrwq7dstEwCEpCABCQgAQlIYAQCEYUIQzxkiMN8EIj9zyYBNtU+PHixA4GHfdiKzU7fHKESGEWn8LMSSEACEpCABCQgAQlIYEsCCLExPoq5LYF72GgEFH6joTQiCUhAAhKQgAQkIAEJSEACdRJQ+NVZLlolAQlIQAISkIAEJCABCUhgNAIKv9FQGpEEJCABCUhAAhKQgAQkIIE6CSj86iwXrZKABCQgAQlIQAISkIAEJDAaAYXfaCiNSAISkIAEJCABCUhAAhKQQJ0EFH51lotWSUACEpCABCQgAQlIQAISGI2Awm80lEYkAQlIQAISkIAEJCABCUigTgIKvzrLRaskIAEJSEACEpCABCQgAQmMRkDhNxpKI5KABCQgAQlIQAISkIAEJFAnAYVfneWiVRKQgAQkIAEJSEACEpCABEYjoPAbDaURSUACEpCABCQgAQlIQAISqJOAwq/OctEqCUhAAhKQgAQkIAEJSEACoxFQ+I2G0ogkIAEJSEACEpCABCQgAQnUSUDhV2e5aJUEJCABCUhAAhKQgAQkIIHRCCj8RkNpRBKQgAQkIAEJSEACEpCABOokoPCrs1y0SgISkIAEJCABCUhAAhKQwGgEFH6joTQiCUhAAhKQgAQkIAEJSEACdRJQ+NVZLlolAQlIQAISkIAEJCABCUhgNAIKv9FQGpEEJCABCUhAAhKQgAQkIIE6CSj86iwXrZKABCQgAQlIQAISkIAEJDAaAYXfaCiNSAISkIAEJCABCUhAAhKQQJ0EFH51lotWSUACEpCABCQgAQlIQAISGI2Awm80lEYkAQlIQAISkIAEJCABCUigTgIKvzrLRaskIAEJSEACEpCABCQgAQmMRkDhNxpKI5KABCQgAQlIQAISkIAEJFAnAYVfneWiVRKQgAQkIAEJSEACEpCABEYjoPAbDaURSUACEpCABCQgAQlIQAISqJOAwq/OctEqCUhAAhKQgAQkIAEJSEACoxFQ+I2G0ogkIAEJSEACEpCABCQgAQnUSUDhV2e5aJUEJCABCUhAAhKQgAQkIIHRCCj8RkNpRBKQgAQkIAEJSEACEpCABOokoPCrs1y0SgISkIAEJCABCUhAAhKQwGgEFH6joTQiCUhAAhKQgAQkIAEJSEACdRJQ+NVZLlolAQlIQAISkIAEJCABCUhgNAIKv9FQGpEEJCABCUhAAhKQgAQkIIE6CSj86iwXrZKABCQgAQlIQAISkIAEJDAaAYXfaCiNSAISkIAEJCABCUhAAhKQQJ0EFH51lotWSUACEpCABCQgAQlIQAISGI2Awm80lEYkAQlIQAISkIAEJCABCUigTgIKvzrLRaskIAEJSEACEpCABCQgAQmMRkDhNxpKI5KABCQgAQlIQAISkIAEJFAnAYVfneWiVRKQgAQkIAEJSEACEpCABEYjoPAbDaURSUACEpCABCQgAQlIQAISqJOAwq/OctEqCUhAAhKQgAQkIAEJSEACoxFQ+I2G0ogkIAEJSEACEpCABCQgAQnUSUDhV2e5aJUEJCABCUhAAhKQgAQkIIHRCCj8RkNpRBKQgAQkIAEJSEACEpCABOokoPCrs1y0SgISkIAEJCABCUhAAhKQwGgEFH6joTQiCUhAAhKQgAQkIAEJSEACdRJQ+NVZLlolAQlIQAISkIAEJCABCUhgNAIKv9FQGpEEJCABCUhAAhKQgAQkIIE6CSj86iwXrZKABCQgAQlIQAISkIAEJDAaAYXfaCiNSAISkIAEJCABCUhAAhKQQJ0EFH51lotWSUACEpCABCQgAQlIQAISGI2Awm80lEYkAQlIQAISkIAEJCABCUigTgIKvzrLRaskIAEJSEACEpCABCQgAQmMRkDhNxpKI5KABCQgAQlIQAISkIAEJFAnAYVfneWiVRKQgAQkIAEJSEACEpCABEYjoPAbDaURSUACEpCABCQgAQlIQAISqJOAwq/OctEqCUhAAhKQgAQkIAEJSEACoxFQ+I2G0ogkIAEJSEACEpCABCQgAQnUSUDhV2e5aJUEJCABCUhAAhKQgAQkIIHRCCj8RkNpRBKQgAQkIAEJSEACEpCABOokoPCrs1y0SgISkIAEJCABCUhAAhKQwGgEFH6joTQiCUhAAhKQgAQkIAEJSEACdRJQ+NVZLlolAQlIQAISkIAEJCABCUhgNAIKv9FQGpEEJCABCUhAAhKQgAQkIIE6CSj86iwXrZKABCQgAQlIQAISkIAEJDAaAYXfaCiNSAISkIAEJCABCUhAAhKQQJ0EFH51lotWSUACEpCABCQgAQlIQAISGI2Awm80lEYkAQlIQAISkIAEJCABCUigTgIKvzrLRaskIAEJSEACEpCABCQgAQmMRkDhNxpKI5KABCQgAQlIQAISkIAEJFAnAYVfneWiVRKQgAQkIAEJSEACEpCABEYjoPAbDaURSUACEpCABCQgAQlIQAISqJOAwq/OctEqCUhAAhKQgAQkIAEJSEACoxFQ+I2G0ogkIAEJSEACEpCABCQgAQnUSUDhV2e5aJUEJCABCUhAAhKQgAQkIIHRCCj8RkNpRBKQgAQkIAEJSEACEpCABOok8P8B1ZSLm4WWsxsAAAAASUVORK5CYII=" style="margin-left: auto; margin-right: auto;" width="715" /></td></tr><tr><td class="tr-caption" style="text-align: center;">Internal domain specific language<br /></td></tr></tbody></table><div><br /></div><p></p><h3 style="text-align: left;"><span style="font-size: medium; font-weight: normal;">Internal DSL can be also seen as a library implemented on top of host language.</span> </h3><h3 style="text-align: left;"><span style="font-size: x-large;">External</span></h3><p>External DSL is not written in the same host language , it can be written in some scripting language and then parsed and compiled. </p><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><img alt="" height="308" src="data:image/png;base64,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" style="margin-left: auto; margin-right: auto;" width="680" /></td></tr><tr><td class="tr-caption" style="text-align: center;">External domain specified language<br /></td></tr></tbody></table><p> Building external DSL is more involved , it has various new components like lexical analyzer , parser , compiler , code generator.</p><p>DSL does not have to be textual , for some domains it makes sense to create visual DSL..</p><p><br /></p><h2 style="text-align: left;">Lets gets started</h2><div><p>Enough of talking! show me some DSL.</p><p>All the examples shown are based on java, although java is not the best language to build DSL but a lot is possible with such a non expressive language. </p></div><h3 style="text-align: left;">Rule engine</h3><div>Rule engine is good candidate for DSL because non tech stakeholder of product understand domain very well, any simplification for business rules creation will helps with collaboration.</div><div><br /></div><div><br /></div><script src="https://gist.github.com/ashkrit/5213fc7898eb15f72f69cef263558788.js"></script><div>This is a general purpose rule engine that allows clients to define rule and action to take when rule is satisfied. In the above example rule engine is used to decide the discount rate for FX transactions.</div><div>An Important design goal about any DSL is that it must have vocabulary from domain and it should be expressive. Expressiveness is a subjective thing and different languages have different support, so host language selection plays an important role. </div><h3 style="text-align: left;"><br /></h3><h3 style="text-align: left;">State machine</h3><div>State machine is another general purpose thing that most applications use, every mutation in a system can be modeled as a finite state machine. </div><div><br /></div><div><br /></div><script src="https://gist.github.com/ashkrit/98f6ba37287593ae0b9fb9949ed68efc.js"></script><div><div>This example takes the popular <a href="https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern" id="gmail-https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern" target="_blank">Circuit Breaker Design Pattern</a> and is implemented as a state machine. State machine domain vocabulary contains states ,events, transition rules. In-case of circuit breaker possible valid states are Open,Half Open, Closed and events are connect, re-connect</div></div><h3 style="text-align: left;"><br /></h3><h3 style="text-align: left;">Testing framework</h3><div>Testing framework uses DSL extensively to make tests expressive, some of examples of dsl based testing framework are <a href="https://cucumber.io/docs/bdd/better-gherkin/" id="gmail-https://cucumber.io/docs/gherkin/reference/" target="_blank">gherkin(BDD)</a> , <a href="https://www.scalatest.org/" id="gmail-https://www.scalatest.org/" target="_blank">scala test</a> , <a href="https://jasmine.github.io/" target="_blank">jasmine </a>etc. </div><div><br /></div><div><br /><script src="https://gist.github.com/ashkrit/24a6d9397f27e48e5260ac33930b125f.js"></script></div><div>In testing domain specification, scenario & assertions are important concepts. </div><div><br /></div><h3 style="text-align: left;">Trading System</h3><div>Investment banking system has a complex domain, DSL in this area gives huge dividends because we can engage domain experts (i.e traders) when the system is built. </div><div><br /></div><div><br /></div><div><br /><script src="https://gist.github.com/ashkrit/b14aa2b657c13abf5589ec9572e66fc6.js"></script></div><div>Trading system will have Orders, Instrument (i.e Equity , fixed income etc), order type (i.e buy/sell), price, full or partial order.</div><h3 style="text-align: left;"><br /></h3><div><br /></div><h3 style="text-align: left;">How to build </h3><p>Now we have seen some real examples of DSL, let's discuss on how to building one.</p><p></p><ul><li>Domain is the core thing in DSL, getting a language that is based on domain core concepts is very important.</li><li>Implicit context - Remembering context in which terms are used plays an important role in expressiveness.</li><li> Well designed abstraction - users with beginner level knowledge should also be able to use language.</li><li>Tools - Having tools like dsl workbench plays an important role in adoption. </li></ul><h3 style="text-align: left;">Trade off </h3><p>Nothing comes for free and DSL also have some</p><p></p><ul style="text-align: left;"><li>Build complexity.</li><li>Learning curve for users of language </li><li>Exceptions and error handling are not trivial</li><li>Performance issue.</li><li>Integration issue</li><li>Backward compatibility. </li></ul><p></p><h3 style="text-align: left;">Conclusion</h3><div>DSL has huge return on investment when domain is complex.</div><div><br /></div><div>All the code used in post is available @ <a href="https://github.com/ashkrit/dsl" id="https://github.com/ashkrit/dsl" name="https://github.com/ashkrit/dsl" target="_blank">github</a></div>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com0tag:blogger.com,1999:blog-6543397255913469784.post-49611499062613050222020-07-10T00:44:00.002-07:002020-07-10T00:46:42.940-07:00Data encoding and storage<div>Data encoding and storage format is evolving field, it has seen so many changes starting from naive text based encoding to advance compact nested binary format.</div><div><br /></div><table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"><tbody><tr><td style="text-align: center;"><img alt="Encoder and decoder" src="https://www.examrace.com/Study-Material/Computer-Science/posts/Data-Encoding-with-Modulation-Techniques-Computer-Science-YouTube-Lecture-Handouts/Encoder-and-decoder.png" style="margin-left: auto; margin-right: auto;" /></td></tr><tr><td class="tr-caption" style="text-align: center;">Encoding/Decoding<br /></td></tr></tbody></table><div><br /></div><div>Selecting correct encoding/storage format has big impact on application performance and how easily it can evolve. Data encoding has big impact on whether application is backward/forward compatible. </div><div>Selecting right encoding format can be one of the important factor for data driven application agility. </div><div><br /></div><div>Application developer tends to makes default choice of text(xml, csv or json) based encoding because it is human readable and language agonist. </div><div><div>Text format are not very efficient, they are take time time/space and also struggle to evolve. If someone care about efficiency then binary format is the way to go. </div></div><div><br /></div><div>In this post i will compare text vs binary encoding and build simple persistent storage that supports flexible encoding.</div><div><br /></div><div>We will compare popular text/binary encoding like <a href="https://en.wikipedia.org/wiki/Comma-separated_values" target="_blank">csv </a>, <a href="https://en.wikipedia.org/wiki/JSON" target="_blank">json </a> , <a href="https://en.wikipedia.org/wiki/Apache_Avro" target="_blank">avro</a> , <a href="https://github.com/OpenHFT/Chronicle-Wire">chronicle</a> and <a href="https://github.com/real-logic/simple-binary-encoding/wiki">sbe</a></div>
<div><br /> <script src="https://gist.github.com/ashkrit/78251557a50f2748dba6bdefe9dbde35.js"></script> </div><div><br /></div><div><br /></div><div>I will use above Trade object as example for this comparison. </div><div><br /></div><h2 style="text-align: left;">CSV</h2><p style="text-align: left;">It is one of the most popular textual format, it has no support for types and makes no distinction between different type of numbers. One of the major restriction is that it only supports scalar types, if we have to store nested or complex object then custom encoding is required. Column and rows values are separated by deliminator and special handling is required when deliminator is part of column value.</p><p style="text-align: left;">Reader application has to parse text and convert into proper type at read time, it produces garbage and is also CPU intensive.</p><p style="text-align: left;">Best thing is that it can be edit in any text editor. All programming language can read and write CSV.</p><p style="text-align: left;"><br /></p><h2 style="text-align: left;">JSON</h2><div>This is what drives Web today. Majorities of micro services that are user facing are using JSON for REST APIs.</div><div>This address some of the issues with CSV by making distinction between string and number, also support nested types like Map,Array, Lists etc. It is possible to have schema for JSON message but it is not in practice because it takes ways flexible schema. This is new XML these days. </div><div>One of major drawback is size, size of JSON message is more as it has to keep key/attribute name as part of message. I have heard in some document based database attribute names takes up more than 50% of the space, so be careful when you select attribute name in json document. </div><p style="text-align: left;">Both of these text format are very popular inspite of all the inefficiency. Across team if you need any friction less data format interface then go for text based one.</p><h2 style="text-align: left;">Chronicle/Avro/SBE</h2><p style="text-align: left;">These are very popular binary format and very efficient for distributed or trading systems.</p><p style="text-align: left;"><a href="https://github.com/real-logic/simple-binary-encoding">SBE</a> is very popular in financial domain and used as replacement of FIX protocol. I shared about it in post <a href="http://ashkrit.blogspot.com/2018/06/inside-simple-binary-encoding-sbe.html">inside-simple-binary-encoding-sbe</a>.</p><p style="text-align: left;"><a href="https://avro.apache.org/docs/current/">Avro</a> is also very popular and it is built by taking lots of learning from protobuffer and thrift. For row based and nested storage this is very good choice. It supports multiple languages. Avro applies some cool encoding tricks to reduce size of message, you can read about it in post <a href="https://ashkrit.blogspot.com/2015/06/integer-encoding-magic.html">integer-encoding-magic</a><br /></p><p style="text-align: left;"><a href="https://github.com/OpenHFT/Chronicle-Wire">Chronicle-Wire</a> is picking up and i came across this very recently. It has nice abstraction over text and binary message with single unified interface. This library allows to choose different encoding based on usecase. <br /></p><p style="text-align: left;"><br /></p><p style="text-align: left;">Lets look at some number now. This is very basic comparison just on size aspect of message. Run your benchmark before making any selection.</p><p style="text-align: left;"><br /></p><p style="text-align: left;"><br />
<script src="https://gist.github.com/ashkrit/f149f3c4ac49c7a40ea8ca5d302f5b14.js"></script>
</p><p style="text-align: left;"><br /></p><p style="text-align: left;"><br /></p><p style="text-align: left;">We will try to save above 2 records in different format and compare size.</p><p style="text-align: left;"><br />
<script src="https://gist.github.com/ashkrit/d5d7650020de3db44a72058d0c05e37c.js"></script>
</p><p style="text-align: left;">Chronicle is most efficient in this example and i have used RawWire format for this example and it is the most compact option available in library because it only stores data, no schema metadata is stored. </p><p style="text-align: left;">Next one is Avro and SBE, very close in terms of size but sbe is more efficient in terms of encoding/decoding operation.</p><p style="text-align: left;">CSV is not that bad, it took 57 bytes for single row but don't select CSV based on size. As expected JSON takes up more bytes to represent same message. It is taking around 2X more than Chronicle.</p><p style="text-align: left;">Lets look at some real application of these encoding. These encoding can be used for building logs , queues , block storage, RPC message etc.</p><p style="text-align: left;">To explore more i created simple storage library that is backed by file and allows to specific different encoding format.</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: "jetbrains mono", monospace; font-size: 9pt;"><span style="color: #cc7832;">public interface </span>RecordContainer<<span style="color: #507874;">T</span>> <span style="color: #cc7832;">extends </span>Closeable {<br /> <span style="color: #cc7832;">boolean </span><span style="color: #ffc66d;">append</span>(<span style="color: #507874;">T </span>message)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> void </span><span style="color: #ffc66d;">read</span>(RecordConsumer<<span style="color: #507874;">T</span>> reader)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> void </span><span style="color: #ffc66d;">read</span>(<span style="color: #cc7832;">long </span>offSet<span style="color: #cc7832;">, </span>RecordConsumer<<span style="color: #507874;">T</span>> reader)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> default void </span><span style="color: #ffc66d;">close</span>() {<br /> }<br /><br /> <span style="color: #cc7832;">int </span><span style="color: #ffc66d;">size</span>()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span>String <span style="color: #ffc66d;">formatName</span>()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span>}</pre><table class="highlight tab-size js-file-line-container" data-paste-markdown-skip="" data-tab-size="8" style="background-color: white; border-collapse: collapse; border-spacing: 0px; color: #24292e; font-size: 14px; tab-size: 8; text-align: left;"><tbody style="box-sizing: border-box;"><tr style="box-sizing: border-box;"><td class="blob-num js-line-number" data-line-number="2" id="file-recordcontainer-java-L2" style="box-sizing: border-box; color: rgba(27, 31, 35, 0.3); cursor: pointer; font-size: 12px; line-height: 20px; min-width: 50px; padding: 0px 10px; text-align: right; user-select: none; vertical-align: top; white-space: nowrap; width: 50px;"></td><td class="blob-code blob-code-inner js-file-line" id="file-recordcontainer-java-LC2" style="box-sizing: border-box; font-size: 12px; line-height: 20px; overflow-wrap: normal; overflow: visible; padding: 0px 10px; position: relative; vertical-align: top; white-space: pre;"><br /></td></tr></tbody></table><p style="text-align: left;">This implementation allow to append records at the end of buffer and access the buffer from starting or randomly from given message offset. This can seen as append only unbounded message queue, it has some similarity with kafka topic storage.</p><p style="text-align: left;">RandomAccessFile form java allow to map file content as array buffer and after that file content can be managed like any array.</p><p style="text-align: left;">All the code used in this post is available @ <a href="https://github.com/ashkrit/corejava/tree/master/playground/src/main/java/encoding">encoding</a> github</p><p style="text-align: left;"><br /></p><p style="text-align: left;"> </p>Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com0tag:blogger.com,1999:blog-6543397255913469784.post-29537321366012188902020-06-28T09:12:00.000-07:002020-06-28T09:12:47.832-07:00Ship your function<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="font-family: "verdana" , sans-serif;">Now a days function as service(FaaS) is trending in serverless area and it is enabling new opportunity that allows to send function on the fly to server and it will start executing immediately. </span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;"><img alt="Code as data as code." height="480" src="https://image.slidesharecdn.com/codedata-111207112119-phpapp01/95/code-as-data-as-code-72-728.jpg?cb=1323260145" width="640" /></span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">This is helps in building application that adapts to changing users needs very quickly.</span><br />
<span style="font-family: "verdana" , sans-serif;"><a href="https://en.wikipedia.org/wiki/Function_as_a_service">Function_as_a_service</a> is popular offering from cloud provider like Amazon , Microsoft, Google etc.</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">FaaS has lot of similarity with <a href="https://en.wikipedia.org/wiki/Actor_model">Actor model</a> that talks about sending message to Actors and they perform local action, if code can be also treated like data then code can also be sent to remote process and it can execute function locally. </span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">I remember <a href="https://en.wikipedia.org/wiki/Joe_Armstrong_(programmer)">Joe Armstrong</a> talking about how during time when he was building Erlang he used to send function to server to become HTTP server or smtp server etc. He was doing this in 1986!</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">Lets look at how we can save executable function and execute it later.</span><br />
<span style="font-family: "verdana" , sans-serif;">I will use java as a example but it can be done in any language that allows dynamic linking. Javascript will be definitely winner in dynamic linking. </span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;"><span style="font-size: x-large;">Quick revision</span></span><br />
<span style="font-family: "verdana" , sans-serif;"> Lets have quick look at functions/behavior in java</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<script src="https://gist.github.com/ashkrit/19e0ed4dfe5747d88d2bae341a8c281e.js"></script>
<span style="font-family: "verdana" , sans-serif;"><br /></span><span style="font-family: "verdana" , sans-serif;">Nothing much to explain above code, it is very basic transformation.</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;"><span style="font-size: x-large;">Save function</span></span><br />
<span style="font-family: "verdana" , sans-serif;">Lets try to save one of these function and see what happens. </span><br />
<br />
<script src="https://gist.github.com/ashkrit/ab020ff6491b38b4b589851fab6c5bd2.js"></script>
<span style="font-family: "verdana" , sans-serif;"><br /></span><span style="font-family: "verdana" , sans-serif;">Above code looks perfect but it fails at runtime with below error</span><br />
<br />
<blockquote>
<i><span style="color: red;">java.io.NotSerializableException: faas.FunctionTest$$Lambda$266/1859039536
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at faas.FunctionTest.save_function(FunctionTest.java:39)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
</span></i></blockquote>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">Lambda functions are not serializable by default.</span><br />
<span style="font-family: "verdana" , sans-serif;">Java has nice trick about using cast expression to add additional bound, more details are available at <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.16">Cast Expressions</a>.<span id="goog_1746443772"></span></span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">In nutshell it will look something like below</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<script src="https://gist.github.com/ashkrit/5f3068ec4f3613e09c419b20884a2774.js"></script>
<span style="font-family: "verdana" , sans-serif;"><br /></span><span style="font-family: "verdana" , sans-serif;">This technique allows to convert any functional interface to bytes and reuse it later. It is used in JDK at various places like TreeMap/TreeSet as these data structure has comparator as function and also </span><span style="font-family: verdana, sans-serif;">supports serialization</span><span style="font-family: verdana, sans-serif;">.</span><br />
<span style="font-family: "verdana" , sans-serif;"> </span><span style="font-family: "verdana" , sans-serif;"> </span><span style="font-family: "verdana" , sans-serif;"> </span><br />
<span style="font-family: "verdana" , sans-serif;">With basic thing working lets try to build something more useful.</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">We have to hide <b><i>& Serialized</i></b> magic to make code more readable and this can be achieved by functional interface that extends from base interface and just adds Serializable, it will look something like below</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<script src="https://gist.github.com/ashkrit/01171c8e080132b84be5e3f4185b7e5e.js"></script>
<span style="font-family: "verdana" , sans-serif;"><br /></span><span style="font-family: "verdana" , sans-serif;">Once we take care of boilerplate then it becomes very easy to write the functions that are Serialization ready.</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<script src="https://gist.github.com/ashkrit/00f562e64bb8968003ac68827ee951d9.js"></script>
<span style="font-family: "verdana" , sans-serif;"><br /></span><span style="font-family: "verdana" , sans-serif;">With above building block we can save full transformation(map/filter/reduce/collect etc) and ship to sever for processing. This also allows to build computation that can recomputed if required.</span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">Spark is distributed processing engine that use such type of pattern where it persists transformation function and use that for doing computation on multiple nodes. </span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">So next time you want to build some distributed processing framework then look into this pattern or want to take it to extreme then send patched function to live server in production to fix the issue. </span><br />
<span style="font-family: "verdana" , sans-serif;"><br /></span>
<span style="font-family: "verdana" , sans-serif;">Code used in in post is available @ <a href="https://github.com/ashkrit/corejava/tree/master/playground/src/main/java/faas">faas</a></span></div>
Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com0tag:blogger.com,1999:blog-6543397255913469784.post-62199921426478723692020-06-23T08:30:00.001-07:002020-06-23T08:34:40.105-07:00Bit fiddling every programmer should know<div dir="ltr" style="text-align: left;" trbidi="on">
Bit fiddling looks like magic, it allows to do so many things in very efficient way.<br />
In this post i will share some of the real world example where bit operation can be used to gain good performance.<br />
<br />
<img alt="Technology Basics: Bits and Bytes - Business Technology, Gadgets ..." height="400" src="data:image/jpeg;base64,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" width="400" /><br />
<span style="font-size: large;">Bit wise operation bootcamp</span><br />
Bit operator include.<br />
- AND ( &)<br />
- OR ( | )<br />
- Not ( ~)<br />
- XOR( ^)<br />
- Shifts ( <<, >>)<br />
<br />
Wikipedia has good high level overview of <a href="https://en.wikipedia.org/wiki/Bitwise_operation">Bitwise_operation</a>. While preparing for this post i wrote learning test and it is available <a href="https://github.com/ashkrit/corejava/tree/master/playground/src/test/java/bitfiddle/learning">learningtest</a> github project. Learning test is good way to explore anything before you start deep dive. I plan to write detail post on Learning Test later.<br />
<br />
In these examples i will be using below bits tricks as building block for solving more complex problem.<br />
<ul style="text-align: left;">
<li><b style="font-weight: bold;"><i>countBits </i></b><b>- </b>Count number of 1 bits in binary</li>
<li><span style="font-weight: bold;"><b><i>bitParity </i></b>- </span>Check bit added to binary code</li>
<li><span style="font-weight: bold;"><b><i>set/clear/toggle</i></b> - </span>Manipulating single bit</li>
<li><span style="font-weight: bold;"><b><i>pow2 </i></b>-</span> Find next power of 2 and using it as mask.</li>
</ul>
<ul style="text-align: left;">
</ul>
<br />
Code for these function is available @ <a href="https://github.com/ashkrit/corejava/blob/master/playground/src/main/java/bitfiddle/Bits.java">Bits.java</a> on github and unit test is available @ <a href="https://github.com/ashkrit/corejava/blob/master/playground/src/test/java/bitfiddle/tricks/BitsTest.java">BitsTest.java</a><br />
<br />
Lets look at some real world problems now.<br />
<br />
<span style="font-size: x-large;">Customer daily active tracking</span><br />
E-commerce company keep important metrics like which days customer was active or did some business. This metrics becomes very important for building models that can be used to improve customer engagement. Such type of metrics is also useful for fraud or risk related usecase.<br />
Investment banks also use such metrics for Stocks/Currency for building trading models etc.<br />
<br />
Using simple bit manipulation tricks 30 days of data can be packed in only 4 bytes, so to store whole year of info only 48 bytes are required.<br />
<br />
Code snippet<br />
<br />
<script src="https://gist.github.com/ashkrit/836d96992e8d00627202c1680dc49cfa.js"></script>
<br />
Apart from compact storage this pattern have good data locality because whole thing can be read by processor using single load operation.<br />
<br />
<span style="font-size: x-large;">Transmission errors</span><br />
<span style="font-size: small;">This is another area where bit manipulation shines.</span><span style="font-size: medium;"> </span><span style="font-size: small;">Think</span> you are building distributed storage block management software or building some file transfer service, one of the thing required for such service is to make sure transfer was done properly and no data was lost during transmission. This can be done using bit parity(odd or even) technique, it involves keeping number of '1' bits to odd or even.<br />
<br />
<script src="https://gist.github.com/ashkrit/5b26bd115b254eb3b3ebcea030d05e20.js"></script>
<br />
Another way to do such type of verification is <a href="https://en.wikipedia.org/wiki/Hamming_distance">Hamming_distance</a>. Code snippet for hamming distance for integer values.<br />
<br />
<br />
<span style="font-size: x-large;"><br /></span>
<script src="https://gist.github.com/ashkrit/258ba868145b39743d3b5a5badcfbbfa.js"></script>
<span style="font-size: x-large;"><span style="font-size: small;">Very useful way to keep data integrity with no extra overhead.</span></span><br />
<span style="font-size: x-large;">Locks</span><br />
Lets get into concurrency now. Locks are generally not good for performance but some time we have to use it. Many lock implementation are very heavy weight and also hard to share between programs .In this example we will try to build lock and this will be memory efficient lock, 32 locks can be managed using single Integer.<br />
<br />
Code snippet<br />
<br />
<script src="https://gist.github.com/ashkrit/f900f06b6cce2fd9b3ef1753578210ca.js"></script>
This example is using single bit setting trick along with AtomicInteger to make this code threadsafe.<br />
This is very lightweight lock. As this example is related to concurrency so this will have some issues due to false sharing and it is possible to address this by using some of the technique mention in <a href="http://ashkrit.blogspot.com/2013/09/scalable-counters-for-multi-core.html">scalable-counters-for-multi-core</a> post.<br />
<br />
<span style="font-size: x-large;">Fault tolerant disk</span><br />
Lets get into some serious stuff. Assume we have 2 disk and we want to make keep copy of data so that we can restore data incase one of the disk fails, naive way of doing this is to keep backup copy of every disk, so if you have 1 TB then additional 1 TB is required. Cloud provider like Amazon will be very happy if you use such approach.<br />
Just by using XOR(^) operator we can keep backup for pair of disk on single disk, we get 50% gain.<br />
50% saving on storage expense.<br />
<br />
Code snippet testing restore logic.<br />
<br />
<script src="https://gist.github.com/ashkrit/5c8306fc44b1d0cb4b1d1e5ab77ec69a.js"></script>
Disk code is available @ <a href="https://github.com/ashkrit/corejava/blob/master/playground/src/main/java/bitfiddle/apps/RaidDisk.java">RaidDisk.java</a><br />
<br />
<span style="font-size: x-large;">Ring buffer</span><br />
Ring buffer is very popular data structure when doing async processing , buffering events before writing to slow device. Ring buffer is bounded buffer and that helps in having zero allocation buffer in critical execution path, very good fit for low latency programming.<br />
One of the common operation is finding slot in buffer for write/read and it is done by using Mod(%) operator, mod or divide operator is not good for performance because it stalls execution because CPU has only 1 or 2 ports for processing divide but it has many ports for bit wise operation.<br />
<br />
In this example we will use bit wise operator to find mod and it is only possible if mod number is powof2. I think it is one of the trick that everyone should know.<br />
<br />
n & (n-1)<br />
<br />
If n is power of 2 then <b style="font-style: italic;">'x & (n-1)' </b>can be used to find mod in single instruction. This is so popular that it is used in many places, JDK hashmap was also using this to find slot in map.<br />
<br />
<br />
<script src="https://gist.github.com/ashkrit/044aa096c7f5537072dd03581bf2e8e9.js"></script>
<br />
<span style="font-size: x-large;">Conclusion</span><br />
I have just shared at very high level on what is possible with simple bit manipulation techniques.<br />
Bit manipulation enable many innovative ways of solving problem. It is always good to have extra tools in programmer kit and many things are timeless applicable to every programming language.<br />
<br />
All the code used in post is available @ <a href="https://github.com/ashkrit/corejava/tree/master/playground">bits</a> repo.</div>
Ashkrithttp://www.blogger.com/profile/09218886398665853347noreply@blogger.com0