Ayende @ Rahienhttps://www.ayende.com/blog/Ayende @ RahienCopyright (C) Ayende Rahien 2004 - 2021 (c) 202460Reading unfamiliar codebases quickly: LMDB<p style="text-align:left;">Reading code is a Skill (with a capital letter, yes) that is really important for developers. You cannot be a good developer without it.</p><p style="text-align:left;">Today I want to talk about one aspect of this. The ability to go into an unfamiliar codebase and extract <em>one</em>&nbsp;piece of information out. The idea is that we don&rsquo;t need to understand the entire system, grok the architecture, etc. I want to understand one thing about it and get away as soon as I can.</p><p style="text-align:left;">For example, you know that project Xyz is doing some operation, and you want to figure out how this is done. So you need to look at the code and figure that out, then you can go your merry way.</p><p style="text-align:left;">Today, I&rsquo;m interested in understanding how the LMDB project writes data to the disk on Windows. This is because LMDB is based around a memory-mapped model, and Windows <span style="text-decoration:underline;"><a style="color:inherit;" href="https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-mapviewoffile#remarks">doesn&rsquo;t keep the data between file I/O and mmap I/O coherent</a></span>.</p><p style="text-align:left;">LMDB is an embedded database engine (similar to Voron, and in fact, Voron is based on some ideas from LMDB) written in C. If you are interested in it, I wrote <span style="text-decoration:underline;"><a style="color:inherit;" href="https://ayende.com/blog/posts/series/162754/reviewing-lightning-memory-mapped-database-library">11 posts going through every line of code</a></span>&nbsp;in the project. </p><p style="text-align:left;">So I&rsquo;m familiar with the project, but the last time I read the code was over a decade ago. From what I recall, the code is <em>dense</em>. There are about 11.5K lines of code in a single file, implementing the entire thing.</p><p style="text-align:left;">I&rsquo;m using <span style="text-decoration:underline;"><a style="color:inherit;" href="https://git.openldap.org/openldap/openldap/-/blob/master/libraries/liblmdb/mdb.c?ref_type=heads#L3432">the code from here</a></span>.</p><p style="text-align:left;">The first thing to do is find the relevant section in the code. I started by searching for the WriteFile()&nbsp;function, the Win32 API to write. The first occurrence of a call to this method is in the <span style="color:#795e26;">mdb_page_flush </span><span style="text-decoration:underline;"><a style="color:inherit;" href="https://git.openldap.org/openldap/openldap/-/blob/master/libraries/liblmdb/mdb.c?ref_type=heads#L3432">function</a></span>.</p><p style="text-align:left;">I look at this code, and&hellip; there isn&rsquo;t really anything there. It is fairly obvious and straightforward code (to be clear, that is a <em>compliment</em>). I was expecting to see a trick there. I couldn&rsquo;t find it.</p><p style="text-align:left;">That meant either the code had a gaping hole and potential data corruption (highly unlikely) or I was missing something. That led me to a long trip of trying to distinguish between documented <em>guarantees</em>&nbsp;and actual behavior. </p><p style="text-align:left;">The documentation for <span style="text-decoration:underline;"><a style="color:inherit;" href="https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-mapviewoffile">MapViewOfFile</a></span>&nbsp;is pretty clear:</p><blockquote><p style="text-align:left;"><span style="color:#161616;">A mapped view of a file is not guaranteed to be coherent with a file that is being accessed by the </span><a style="color:inherit;" href="https://learn.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-readfile">ReadFile</a><span style="color:#161616;">&nbsp;or </span><a style="color:inherit;" href="https://learn.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-writefile">WriteFile</a><span style="color:#161616;">&nbsp;function.</span></p></blockquote><p style="text-align:left;">I have <span style="text-decoration:underline;"><a style="color:inherit;" href="https://ayende.com/blog/164577/is-select-broken-memory-mapped-files-with-unbufferred-writes-race-condition">my own run-ins with this behavior</a></span>, which was super confusing. This means that I had experimental evidence to say that this is broken. But it didn&rsquo;t make sense, there was no code in LMDB to handle it, and this is pretty easy to trigger. </p><p style="text-align:left;">It turns out that while the documentation is pretty broad about <em>not</em>&nbsp;guaranteeing the behavior, the actual issue only occurs if you are working with remote files or using unbuffered I/O.</p><p style="text-align:left;">If you are working with local files and buffered I/O (which is 99.99% of the cases), then you can rely on this behavior. I found some <span style="text-decoration:underline;"><a style="color:inherit;" href="https://community.osr.com/t/memory-mapped-file-position/13911/2">vague</a></span><span style="text-decoration:underline;"><a style="color:inherit;" href="https://community.osr.com/t/coherency-memory-mapped-file-write/13919/3">references </a></span>to this, but that wasn&rsquo;t enough. There is <span style="text-decoration:underline;"><a style="color:inherit;" href="https://community.osr.com/t/memory-mapped-file-detection-logic/43826/11">this post that is really interesting</a></span>, though.</p><p style="text-align:left;">I pinged Howard Chu, the author of LMDB, for clarification, and he was quick enough to assure me that yes, my understanding was (now) correct. On Windows, you can mix memory map operations with file I/O and get the right results.</p><blockquote><p style="text-align:left;">The documentation appears to be a holdover from Windows 9x, with the NT line always being able to ensure coherency for local files. This is a guess about the history of documentation, to be honest. Not something that I can verify.</p></blockquote><p style="text-align:left;">I had the wrong information in my head for over a decade. I did <em>not</em>&nbsp;expect this result when I started this post, I was sure I would be discussing navigating complex codebases. I&rsquo;m going to stand in the corner and feel upset about this for a while now. </p> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/9000.0.1/themes/prism.min.css" integrity="sha512-/mZ1FHPkg6EKcxo0fKXF51ak6Cr2ocgDi5ytaTBjsQZIH/RNs6GF6+oId/vPe3eJB836T36nXwVh/WBl/cWT4w==" crossorigin="anonymous" referrerpolicy="no-referrer" />https://www.ayende.com/blog/201313-B/reading-unfamiliar-codebases-quickly-lmdb?Key=64d1c380-b6d1-47a0-8ce6-d4cb741659b1https://www.ayende.com/blog/201313-B/reading-unfamiliar-codebases-quickly-lmdb?Key=64d1c380-b6d1-47a0-8ce6-d4cb741659b1Fri, 05 Jul 2024 12:00:00 GMTCode review & Time Travel<p style="text-align:left;">A not insignificant part of my job is to go over code. Today I want to discuss how we approach code reviews at RavenDB, not from a process perspective but from an operational one. I have been a developer for nearly 25 years now, and I&rsquo;ve come to realize&nbsp;that when I&rsquo;m doing a code review I&rsquo;m actually looking at the code from three separate perspectives.</p><p style="text-align:left;">The first, and most obvious one, is when I&rsquo;m actually looking for problems in the code - ensuring that I can understand what is going on, confirming the flow makes sense, etc. This involves looking at the code <em>as it is right now</em>. </p><p style="text-align:left;">I&rsquo;m going to be showing snippets of code reviews here. You are not actually expected to follow the <em>code</em>, only the concepts that we talk about here.</p><p style="text-align:left;">Here is a classic code review comment:</p><p style="text-align:left;"><img src="data:image/png;base64,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CBkUfGPYYDNFREREREStGZsPERERERHFOAYFREREREQxjkEBEREREVGMY1BARERERBTjGBQQEREREcU4BgVERERERDGOQQERERERUYxjUEBEREREFOMYFBARERERxTgGBUREREREMY5BARERERFRjGNQQEREREQU4xgUEBERERHFOAYFREREREQxLq5WMeM+H+/ah28MG2ymGq+8vBylpaU4f/48KisrUV1do+cnJMQjKSkJKSkpaNeuHZKTk/V8IiIiIiK6cJq1pqC0tAwHDx7E0aNHgeoyJJYdw8Gd76Hw7+tRc2o/+vXpgZSOXVCuPnvkiFqmPivrEBERERHRhdNsNQXHjh1Hadl5dOrcAXEq2//uuj9i547tOH+2BIcPHUJ1fCIGDxuOUWNvxTXfHIUUdETZ+TKUlJxCu3Yp6NGju9kSERERERE1pyYPCmpqanD48GHUIAmdOrVD9flTWPrSYnSIP4MjB4tx6shXSIgHahGPM2XlOHjiLDp27YF///kSXO65Aokp7XH85HG0ia9G7969ER/Pbg9ERERERM2pyYMCaQJUWZOEdskpKC05iCWLfoWzJw+ja4dk1FZXYVjaQKT274uU5CScPVeG/cVf4R///BhFx5LwwpKl6NxzAEqrE1BTVYaU+Cr06dPHbJmIiIiIWpsTp8+g5EwpSssrUFlZZea2DElJiSrP2wadOrZDt84dzdyWqUmDAmkydKa0ColJ7dAG5XjzteXY7/0It2aMQd9egxGPWpXZLwXKz6v/SlFRLs2FSnSfg//ZuA09+g/Gz3+1GGW1KSivSURS9Vl0aZ/IpkRERERErYwEA4eOnUJblanu0rE92rdLQVJiAuLi4swnLm2Sha6sqsa50vM4deYcylRQc1mPLi02OGiytjnSQbjkbBkq49qrKK8CB74oxNYtb+OWb49E13ZJqFAnrKK0HDXltaiqUnFIdQJqK2uRpJLQtW0HDOrXA5/v3IHP/rkD1dXVqKoByuI6qsixjJ2PiYiIiFqRLw8fx7GTZzCwT08M7t8bXTt3QJukxBYTEAhJq6RZ0i7HIMcixyTH1hI1WVBw+vQpnK9tqzP0NSpq+vPq1ejVrTO6dkhBG1SiquyUmn8GNbXl6iTWoKr6vIquzqO6pgIJiTVol1SrosS2eOedTaioqFCBQxUqKmtQVp2st11vu5dhsicdk5cU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" style="float: right"/></p><p style="text-align:left;">There is some duplicated code that we need to manage. Another comment that I liked is this one, pointing out a potential optimization in the code:</p><p style="text-align:left;"><img src="data:image/png;base64,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" style="float: right"/></p><p style="text-align:left;">If we define the code using the <em>static</em>&nbsp;keyword, we&rsquo;ll avoid delegate allocation and save some memory, yay!</p><p style="text-align:left;">It gets more interesting when the code is correct and proper, but may do something weird in some cases, such as in this one:</p><p style="text-align:left;"><img src="data:image/png;base64,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" style="float: right"/></p><p style="text-align:left;">I really love it when I run into those because they allow me to actually explore the problem thoroughly. Here is an even better example, this isn&rsquo;t about a problem in the code, but a discussion on its impact. </p><p style="text-align:left;"><img src="data:image/png;base64,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ZCIiIiKicy7qgvxAuqlWfGnSPXf5ffjWOd18taa9Tz4RERERUbSLuiA/IT4eTe4mNWaSrfPObjfybTt6AG/0vxfTdPIOgHwAVyfTlusgIiIiIopWURfNJiclGq+51FndbvSg/p1338fylb813rojX5Up++IXaa/MlN+Vg34HQJJpy3UQEREREUWrqAvy01KTUaP90S3pxhuuNwJ2PYiXb9q5LGOw0XovX5Up340/Y9r31VyI4P8143+5rE6mLddBRERERBStou4v3so/VHXg0HH07d1dTTHJYF622stgXr4HPxj5h7Dkm3ZkH31nd50Dh46JtLuJ7WegT0RERETRKfpa8kXwnZiY4PMHq2SwLoeiki1GsK933bHIaTK4l4N8faYzwJdpyrQZ4BMRERFRNIu6lnyp6mwNDh456dOaL8n+9zLQt96ZL7vsyH738sFc2YIvBWrtl634fXp2Qce0VDWFiIiIiCj6RGWQLx08egINjY3o3iVdTfGy+ucfO37cCOxlwC/fwCMf0JV98K0LAN2xk6eQmJCAPj185xERERERRZOoDfKlzw4cMd6E06VzRzWleU6erjL+ku6Avj3VFCIiIiKi6BXVL4S/pE93IziXrfDNJZeVaci0iIiIiIhiQVS35Ftk152q6lqkd+qAjh3C608vH7I9daZafD+FXXSIiIiIKKbERJAvyYdxj544g4aGRqSlpSBVrD8pMQHxcebNCvmXbOUfupLvwT97ttZ4i06Prp34kC0RERERxZyYCfIt8j36spX+bE2d0Q2nsanJmJ4QH2/035d/6Eq29vM1mUREREQUq2IuyCciIiIiutBF9YO3RERERETki0E+EREREVGMYZBPRERERBRjGOQTEREREcUYBvlERERERDGGQT4RERERUYxhkE9EREREFGMY5BMRERERxRgG+UREREREMYZBPhERERFRjGGQT0REREQUYxjkExERERHFGAb5REREREQxhkE+EREREVGMiXML6nPEamtrUV1djZqaGjQ0NKDqbJ0xPSEhHp06pCI1NRUdOnRASkqKMZ2IiIiIiNpes4L86uqzOHnyBOSSqSkJaDpzANWHdqPm1CG445KQ0PEi9Bj8FSCxA86ePYu4OKBLl64i4E9TKRARERERUVuJOMg/cuQoGhrq0SEtDSf27MC+/3sTDbVn0dTYiLS0ZCQlJaCq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" style="float: right"/></p><p style="text-align:left;">RavenDB has been around for over 15 years, and being able to go back and look at those conversations in a decade or so is invaluable to understanding what is going on. It also ensures that we can share current knowledge a lot more easily.</p><p style="text-align:left;">Speaking of long running-projects, take a look at the following comment:</p><p style="text-align:left;"><img src="data:image/png;base64,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mhSQXrKvzYNHGhQ4hLTvktz/tNmupleuj9HUioiIiIjahPYZNOzOQbalA67eTGhk6sXaUPr4Scid5aOvgzRhPDLXBdkuX3W4zjWta+Ori1GovuvBCkx9BgKRNRNpSHa16NmEwqBqGjYh2xQkOFLT1LcQhbLv0uBxGI3FHmlt0GtM2BGaiIiIqPVrn0GDyMzDo/mR7Kzral8/4UW9Q7K5WY3rLUXmTtTGx9/fUhC/1ToLu9dVON7cp0H2YXgLOQ5rkx93LYDeZ8D99iTTsPbbfCComoaLkex8xLV8reN1g/6eQyj7LsnaDev+yw87QhMRERG1LRH1gvoelC937cWVQwapofA7W12jvgGdYmPUNyIiIiIiains00BERERERLYYNBARERERkS0GDUREREREZItBAxERERER2WLQQEREREREthg0EBERERGRLQYNRERERERki0EDERERERHZYtBARERERES2GDQQEREREZEtBg1ERERERGSLQQMREREREdli0EBERERERLYYNBARERERkS0GDUREREREZItBAxERERER2WLQQEREREREthg0EBERERGRLQYNRERERERki0EDERERERHZYtBARERERES2GDQQEREREZEtBg1ERERERGSLQQMREREREdli0EBERERERLYYNBARERERka12HjRUYPEz32PxAfH1wBFM/D/H9dEe3vk/e/DoR2rAi1zGHsT5mFfOF/ew+limq3nUtIlrK9R4yTot7pkj2KWmEBERERG1hHYeNFSjBHG4tb/4WloD9InVRxtkICEy7lO+UcO+fHQcT5er7y56xn8KeqLylUv0z//uoabJYOIQnk40pvXEpVsOuYMSsbySdDWP+LyZeBpX+QlmiIiIiIiaQ/sOGg6cw3eJHTBEfH3nixrcekO8Pl6SAcPvK3Hrb/rhD73VOC8iONhYgxmXR6lhRQYS6IIvTIGC23Fs/SYKf7jDmNYDM8ZEYfkXKjD4STJe/on+Vbrjji645ZsKvKOGiYiIiIiaW/sMGlQNQtzvT4sM/A9aM6Ap39Ti6d/LJkGlega9f1+sf+VizJa1EH7sWnsUTyd2w4w+aoTyzhdVmJHeVwtGvHxUgeW9Ve2GMuSGOP+BQWkNtvaOwaVqkIiIiIioubXPoEELCC7Bm5cDM36pNxGa0bsLvtCaBCXjDvUzWyLw+L+3xOBNr9qECpSURSEFKjDRPqrfRMhkTUYVbhnWxXcAQkRERETUDNpl0GB0UJZ9FZb/RX7/AcvLT+MqMc7aKdkfkZlfeRqX/tJfgFGLpzcC/6/ql1D5yxg8/XtVgxE02S9C7/uwfrKp2RQRERERUTNrl0HDHf/bWrvwxZgoVeNwSVAZdKMjs7nvgSdL86Sf9MAfeldhq983MAnmJkha86lDeodon/0iiIiIiIiaT/vtCC37FqhO0N8dBVKS9dGByY7M4h/VF0J+rtpSq4ZlbUI8UhL1X/qUHINbymvwnRqUdm2vxFa1LXoH7BrMEAGMXVBCRERERNRc2m3QsOugyJhfJUvxZR+EGKTYdHi26oGXjWZH6iNrKnC5fIWq3lzpjqs6YvlfTM2RtNeydsQtMgjo3xczLq/CFNdrVI9juQg69G0B3nnnNDCmR3D9KoiIiIiImkFEvaC+B+XLXXtx5ZBBaij8zlbXqG9Ap9gY9a0l6X0LZFMhfyX/u9Z+j6uOdrM2JfqoFHF/qVIDHfGmpYO16q+g/r6DbBplLFv2t/D1dyHMvyEiIiIiak4MGoiIiIiIyFb77dNARERERERBYdBARERERES22mXzJPnGo7bo1DIHoiMj1BARERERUfNgnwYiIiIiIrLF5klERERERGSLQQMREREREdli0EBERERERLYYNBARERERkS0GDUREREREZItBAxERERER2WLQQEREREREthg0EBERERGRLQYNRERERERki0EDERERERHZYtBARERERES2GDQQEREREZEtBg1ERERERGSLQQMREREREdkA/n/AwRrCQO/KsgAAAABJRU5ErkJggg==" style="float: right"/></p><p style="text-align:left;">Here we need to provide some context to explain. The <em>_caseInsensitive</em>&nbsp;variable here is a concurrent dictionary, and the change is a pretty simple optimization to avoid the annoying KeyValuePair overload. Except&hellip; this code is there intentionally, we use it to ensure that the removal operation will only succeed if <em>both</em>&nbsp;the key and the value match. There was an old bug that happened when we removed blindly and the end result was that an updated value was removed. </p><p style="text-align:left;">In this case, we look at the code change from a historical perspective and realize that a modification would reintroduce old (bad) behavior. We added a comment to explain that in detail in the code (and there already was a test to catch it if this happens again). </p><p style="text-align:left;">By far, the most important and critical part of doing code reviews, in my opinion, is not focusing on what <em>is</em>&nbsp;or what <em>was</em>, but on what <em>will </em>be. In other words, when I&rsquo;m looking at a piece of code, I&rsquo;m considering not only what it is doing right now, but also what we&rsquo;ll be doing with it in the future. </p><p style="text-align:left;">Here is a simple example of what I mean, showing a change to a perfectly fine piece of code:</p><p style="text-align:left;"><img src="data:image/png;base64,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kceUOhJM+z68gp+59sHWNehG/C5vBO7Q1708FlPuI3DMK3/D8cUUj2F3AC9MkpaEQsTf0VhPdz3q85al5/D5UvORoI5HfjpaKB7OBt6Y4aEVQpXH0uUpuIaffSIiuogiziv6tdOm3J0o6XpOj1FN0BYt9CsKffL0nvux95GKu8RQbcLzSkRENQMTgFqCCUC4kcdtfoJr8p5Gx7ThXm7glT707OZVW8h9Oq/gacz1csM2ERHRhcIEoJZgAkBEREREwcB7AIiIiIiIwggTACIiIiKiMMIEgIiIiIgojHi9B+CKzh30GBERERERhQq2ABARERERhREmAEREREREYSRoXYCObluL1Z/k4EyXAbjzZ50Qq6f78vnnnyM3NxfHjgXvz+wTERERXQyNGzdG165d8ZOf/ERPIaoJzmLnJ0uQsa0ukn/WF326NAlCC0DJfmzKeAfvfLQRu0+exf6Dh3FSz/Ll/fffx5dffsngn4iIiEKCxDQS20iMQ1RznMThg/tx9uRubPxIxewZm6qWAJws+BLLXn8HH+eqlepp/pCa/8LCQj1GREREFDokxpFYh6jmOYv9uR9XPgHY//VSvLHkc+z0p7rfjXT7ISIiIgpVjHWoJqtkAnAIhTt3B1Trb8VuP0RERBTKGOtQTVbJBKA54jt18OtGX0/kJhkiIiKiUMVYh2qySncBanXNHXjwzp+gUwM9IQByhzwRERFRqGKsQzVZlW4CbtD+egx56D7c3LVVQK0B8nis+Ph4PUZEREQUOiTG4aNAqWaKRauuN1ctATBEt0L3Affhvlt7oEMDtdIWzeBPo8Bdd92F66+/nk1kREREFBIkppHYRmIcopqjAZq1aIXYBh3Q41YVsw/oHrw/BEZERERERDVf1VsAiIiIiIio1mACQEREREQURrx2ASIiIiIiotDjNQHo3rWTHiMiIiIiolDBLkBERERERGGECQARERERURhhAkBEREREFEaYABARERERhREmAEREREREYYQJABERERFRGGECQEREREQURpgAEBERERGFESYARERERERhhAkAEREREVEYYQJARERERBRGmAAQEREREYURJgBERERERGGECQARERERURiJOK/o106bcneie9dOeqzqzp49i1OnTuHMmTMoLi5GaWmZMT0qKhIxMTGoW7cu6tevj9jYWGM6ERERERFVj2pNAE6d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wbN59uMwbl52/p0AdZyvjAec8zPRa+4gY45BlWHeWMf2zZuife4zERERhZyI84p+7bQpdye6d62+XvCnz7r+WFS92Bj9KriOHDuhX9GF1rRxQ/0qdBlPDcofzRthiYiIqNYJ2RaAqCj2broYWO5ERERENVvIRmt1YqqnZYF8Y7kTERER1Wwh2wVIHD95CqWlZXqMqpvU/jdqUF+PEREREVFNFNL9NRrUq8suKReIlLOUNxERERHVbCEdHUdGmjXS9erGMhGoJlKuUr5SzlLeRERERFSzhXQXICIiIiIismOVLRERERFRGGECQEREREQURpgAEBERERGFESYARERERERhhAkAEREREVEYYQJARERERBRGmAAQEREREYURJgBERERERGGECQARERERURhhAkBEREREFEaYABARERERhREmAEREREREYYQJABERERFRGGECQEREREQURpgAEBERERGFkYjzin7ttCl3J7p37aTHgu/02WL9CqgXG6NfVZ+S0jKUqqGs/KESEREREYWEyIgIREVFIloNvoR0C4AE/GfPlaC4pJTBPxERERGFNIl3Je6V+NdX7BvSCUBxMQN/IiIiIgovRiKg4mBvQjYBkG4/DP6JiIiIKBxJHCzxsCchmwBIn38iIiIionDlLR4O2QSAtf9EREREFM68xcMhfQ8AERERERHZMQEgIiIiIgojIft3AKzbICIiIiKqLkePHsXJU6dQfK4YHkLroIiIiEBMnRg0qF8fTZo00VMr5inWZgJARERERFQJxcUlOHDwgPHHtxo1aIi69eoiOjpazw2ukpISI8E4eOiQkQy0bNESMTEVb8tTrM0uQERERERElXDgwAHE1qmD1nGt0aBBA0RGRqGs7Hy1DLLuunXrIr5dO6MVQLZdWUwAiIiIiIgCJN1+zqv/WrRoYXT7uRDPn5RtyLaaNm1qbFv2oTKYABARERERBejEyZO4pHnzauvz74tsU7Yt+1AZTACIiIiIiAJUUlxidMm5WGTbsg+VwQSAiIiIiChAF6Pm311l94EJABERERFRGGECQEREREQURvh3AIiIiIiIArRr13fo2PFSPea/7fk7kTb//+kxz5o3a4r+qTfjx9derad45s8+8A+BEREREREFQWUTgGf+9DIuad4MiQneY+0v/7PB+Pfp3z9m/OtNZRMAdgGqyKqpSL58KrL0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" style="float: right"/></p><p style="text-align:left;">The problem is that the if statement will call <em>InitializeCmd</em>(), but we previously called it <em>using a different condition</em>. We are essentially testing for the same thing using two different methods, and while currently we end up with the same situation, in the future we need to be aware that this may change. </p><p style="text-align:left;">I believe one of the major shifts in my thinking about code reviews came about because I mostly work on RavenDB, and we have kept the project running over a long period of time. Focusing on making sure that we have a sustainable and maintainable code base over the long haul is <em>important. </em>Especially because you need to experience those benefits over time to really appreciate looking at codebase changes from a historical perspective.</p> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/9000.0.1/themes/prism.min.css" integrity="sha512-/mZ1FHPkg6EKcxo0fKXF51ak6Cr2ocgDi5ytaTBjsQZIH/RNs6GF6+oId/vPe3eJB836T36nXwVh/WBl/cWT4w==" crossorigin="anonymous" referrerpolicy="no-referrer" />https://www.ayende.com/blog/200577-B/code-review-time-travel?Key=3ec75656-3dca-4436-91a4-85f16cbae154https://www.ayende.com/blog/200577-B/code-review-time-travel?Key=3ec75656-3dca-4436-91a4-85f16cbae154Fri, 19 Jan 2024 12:00:00 GMTProduction postmortem: The allocating query<p>A customer was experiencing large memory spikes in some cases, and we were looking into the allocation patterns of some of the queries that were involved. One of the things that popped up was a query that allocated just under 30GB of managed memory during its processing.</p> <p>Let me repeat that, because it bears repeating. That query allocated 30(!) GB(!) during its execution. Now, that doesn&rsquo;t mean that it was <em>consuming</em> 30 GB, it was just the allocations involved. Most of that memory was immediately discarded during the operation. But 30 GB of garbage to cleanup puts a lot of pressure on the system. We took a closer look at the offensive query. It looked something like this:</p> <blockquote> <p><span style="color: #0000ff;">from index</span> &ldquo;Notifications/RoutingAndPriority&rdquo; <br /><span style="color: #0000ff;">where</span> <span style="color: #9b00d3;">startsWith</span>(Route, $routeKeyPrefix)<span style="color: #0000ff;"> <br />order by</span> Priority <span style="color: #0000ff;">desc</span></p> </blockquote> <p>That does <em>not</em> seem like a query that should be all that expensive. But details matter, so we dove into this. For this particular query, the routes are hierarchical structures that are <em>unique </em>for each message. Something like:</p> <ul> <li>notifications/traffic/new-york-city/67a81019-941b-4d04-a0db-0559ed45343c</li> <li>notifications/emergency/las-vegas/0a8e18fb-563b-4b6a-8e93-e10e08239656</li> </ul> <p>And the queries that were generated were using the city &amp; topic to filter the information that they were interested in.</p> <p>The customer in question had a <em>lot</em> of notifications going on at all times. And each one of their Routes was unique. Internally, RavenDB uses Lucene (currently <img class="wlEmoticon wlEmoticon-smile" src="https://ayende.com/blog/Images/Open-Live-Writer/Production-postmortem_3E51/wlEmoticon-smile_2.png" alt="Smile" /> ) to handle searches, and Lucene is using an inverse index to execute queries.</p> <p>The usual way to think about is like this:</p> <p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Production-postmortem_3E51/image_2.png"><img style="margin: 0px; display: inline; background-image: none;" title="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Production-postmortem_3E51/image_thumb.png" alt="image" width="234" height="358" border="0" /></a></p> <p>We have a list of terms (Brown, Green &amp; Purple) and each of them has a list of the matching documents that contain the particular term.</p> <p>The process of issuing a prefix query then is easy, scan all entries that match the prefix and return their results. This is indeed what Lucene is doing. However&hellip; while it is <em>doing</em> that, it will do something like this:</p> <blockquote> <script src="https://gist.github.com/ayende/144f242419ffc03cad35603adec2263e.js"></script> </blockquote> <p>Pay close attention to what is actually happening here. There are <em>two</em> enumerators that we work with. One for the terms for the field and one for the documents for a specific term.</p> <p>All of this is perfectly reasonable, but there is an issue. What happens when you have a <em>lot</em> of unique values? Well, then Lucene will have a lot of iterations of the loop. In this case, each term has just a single match, and Lucene is pretty good at optimizing search by specific term.</p> <p>The actual problem is that Lucene <em>allocates</em> a string instance for <em>each term</em>. If we have 30 million notifications for New York&rsquo;s traffic, that means that we&rsquo;ll allocate 30 million strings during the processing of the query. We aren&rsquo;t <em>retaining</em> these strings, mind. They&rsquo;ll be cleaned up by the GC quickly enough, but that is an additional cost that we don&rsquo;t actually want.</p> <p>Luckily, in this case, there is a much simple solution. Given that the pattern of the route is known, we can skip the unique portion of the route. That means that in our index, we&rsquo;ll do something similar to:</p> <blockquote> <p>Route = doc.Route.Substring(0, doc.Route.LastIndexOf('/') + 1)</p> </blockquote> <p>Once that is done, the number of <em>unique</em> matches there would be negligible. There would be no more allocations galore to observe and overall system performance is much improved.</p> <p>We looked into whether&nbsp;there is something that we can do with Lucene to avoid this allocations issue, but it is endemic to the way the API works. The longer term plan is to fix that completely, of course. We are making great strides there already <img class="wlEmoticon wlEmoticon-smile" src="https://ayende.com/blog/Images/Open-Live-Writer/Production-postmortem_3E51/wlEmoticon-smile_2.png" alt="Smile" />.</p> <p>In short, if you are doing startsWith() queries or similar, pay attention to the number of unique terms that you have to go through. A simple optimization on the index like the one above can bring quite a bit of dividends.</p>https://www.ayende.com/blog/197953-A/production-postmortem-the-allocating-query?Key=8a19a5e6-c99f-484b-b58c-f0caf6e65221https://www.ayende.com/blog/197953-A/production-postmortem-the-allocating-query?Key=8a19a5e6-c99f-484b-b58c-f0caf6e65221Fri, 05 Aug 2022 12:00:00 GMTReviewing code isn’t a binary operation<p>I want to comment on the following tweet:</p><blockquote><blockquote class="twitter-tweet"><p lang="en" dir="ltr">A rather banal “thought experiment:”<br><br>What if the only code we could review was tests and interface definitions?<br><br>What would that force us to specify at the interface and behaviour level, rather than just the implementation level?</p>— Reginald Braithwaite (@raganwald) <a href="https://twitter.com/raganwald/status/1345428908766875648?ref_src=twsrc%5Etfw">January 2, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></blockquote><p>When I read it, I had an immediate and visceral reaction. Because this is one of those things that <em>sound</em> nice, but is actually a horrible dystopian trap. It confused two very important concepts and put them in the wrong order, resulting in utter chaos. </p><p>The two ideas are “writing tests” and “producing high quality code”. And they are usually expressed in something like this:</p><blockquote><p>We write tests in order to product high quality code. </p></blockquote><p>Proper tests ensure that you can make forward progress without having regressions. They are a tool you use to ensure a certain level of quality as you move forward. If you assume that the target is the <em>tests</em> and that you’ll have high quality code because of that, however, you end up in weird places. For example, take a look at the following set of stairs. They aren’t going anywhere, and aside from being decorative, serves no purpose.</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing_9EF5/image_2.png"><img width="596" height="438" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing_9EF5/image_thumb.png" border="0"></a></p><p>When you start considering tests themselves to be the goal, instead of a means to achieve it, you end up with decorative tests. They add to your budget and make it harder to change things, but don’t benefit the project. </p><p>There are a <em>lot</em> of things that you are looking for in code review that shouldn’t be in the tests. For example, consider the error handling strategy for the code. We have an invariant that says that exceptions may no escape our code when running in a background thread. That is because this will kill the process. How would you express something like that in a test? Because you end up with an error raised from a write to a file that happens when the disk is full that kills the server. </p><p>Another example is a critical piece of code that needs to be safely handle out of memory exceptions. You can test for that, sure, but it is hard and expensive. It also tend to freeze your design and implementation, because you are now testing implementation concerns and that make it very hard to change your code. </p><p>Reviewing code for performance pitfalls is also another major consideration. How do you police allocations using a test? And do that without killing your productivity? For fun, the following code allocates:</p><blockquote><p>Console.WriteLine(1_000_000);</p></blockquote><p>There are ways to monitor and track these kind of things, for sure, but they are very badly suited for repeatable tests. </p><p>Then there are other things that you’ll cover in the review, more tangible items. For example, the quality of error messages you raise, or the logging output. </p><p>I’m not saying that you can’t write tests for those. I’m saying that you shouldn’t. That is something that you want to be able to change and modify quickly, because you may realize that you want to add more information in a certain scenario. Freezing the behavior using tests just means that you have more work to do when you need to make the changes. And reviewing just test code is an even bigger problem when you consider that you need to consider interactions between features and their impact on one another. Not in terms of correctness, you absolutely need to test that, but in terms of behavior.</p><p>The interleaving of internal tasks inside of RavenDB was careful designed to ensure that we’ll be biased in favor of user facing operations, starving background operations if needed. At the same time, it means that we need to ensure that we’ll give the background tasks time to run. I can’t really think about how you would write a test for something like that without basically setting in stone the manner in which we make that determination. That is something that I explicitly don’t want to do, it will make changing how and what we do harder. But that is something that I absolutely consider in code reviews.</p>https://www.ayende.com/blog/192834-B/reviewing-code-isnt-a-binary-operation?Key=badef09f-84dc-4cf1-84c9-d063c947c74dhttps://www.ayende.com/blog/192834-B/reviewing-code-isnt-a-binary-operation?Key=badef09f-84dc-4cf1-84c9-d063c947c74dTue, 05 Jan 2021 12:00:00 GMTReviewing mimalloc: Part II<p>In my last post, I looked into the infrastructure of mimalloc, to be frank. A few details on how it is actually hooking the system allocator when used, mostly. I’m still using what is effectively a random scatter/gather approach to the codebase. Mostly because it is small. I’m trying to… grok it would be the best term, I guess. I’m going over the code because it also give me a good idea about practices in what seems to be a <em>damn good</em> C codebase. </p><blockquote><p>I should mention that I drove right into the code, there is also the <a href="https://www.microsoft.com/en-us/research/uploads/prod/2019/06/mimalloc-tr-v1.pdf">tech report</a>, which I intend to read, but only after I got through enough of the code to get a good feeling for it. </p></blockquote><p>I run into the code in the <em>options.c</em> file, for instance:</p><blockquote><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-mimalloc-Part-II_13640/image_2.png"><img width="931" height="733" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-mimalloc-Part-II_13640/image_thumb.png" border="0"></a></p></blockquote><p>This is a really nice way to get configuration values from the user. What I find really interesting, to be frank, is not the actual options, which would be interesting later on, but the fact that this is such a nice way to represent things in a readable manner. </p><p>I’m doing similar things in C# (a much higher level language) to create readable options (typically using dictionary &amp; dictionary initializer). I like how the code is able to express things so succinctly in a language with far fewer features.</p><p>However, the <em>order</em> of parameters is critical (is should match the <em>mi_option_t</em> enum values), and there is no way to express this in the code. </p><p>I also liked this code, which is part of reading configuration values from env variables:</p><blockquote><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-mimalloc-Part-II_13640/image_4.png"><img width="745" height="228" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-mimalloc-Part-II_13640/image_thumb_1.png" border="0"></a></p></blockquote><p>I like that this is using <em>strstr()</em> in reverse in this manner. It is really elegant.</p><p>Going over the OS abstraction layer, on the other hand, show some granliness, take a look here:</p><blockquote><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-mimalloc-Part-II_13640/image_8.png"><img width="570" height="430" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-mimalloc-Part-II_13640/image_thumb_3.png" border="0"></a></p></blockquote><p>I actually simplified the code abit, because it also had #if there for BSD, Linux, etc. I find it harder to follow this style, maybe adding indentation would help, but I have had to read this function multiple times, filtering for specific OSes to get it right.</p><p>I did find this tidbit, which is interesting:</p><blockquote><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-mimalloc-Part-II_13640/image_6.png"><img width="725" height="361" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-mimalloc-Part-II_13640/image_thumb_2.png" border="0"></a></p></blockquote><p>This is attempting to do allocation with exclusive access. I wonder how this is actually used for. It looks like mimalloc is attempting to allocate in specific addresses, so that should be interesting.</p><p>Indeed, in <em>_mi_os_reset()</em> they will explicitly ask the OS to throw the memory away by calling MADV_FREE or MEM_RESET. I find this interesting, because this let the OS know that the memory can be thrown away, but the allocation still persists. I’m currently looking into some fragmentation issues in 32bits, which <em>won’t</em> be helped by this scenario. Then again, I don’t think that mimalloc is primarily intended for 32 bits systems (I can see code handling 32 bits, but I don’t think this is the primary use case or that 32 bits had a lot of attention).</p><p>The <em>mi_os_alloc_aligned_ensured()</em> method call is doing some interesting things. If I need a 16MB buffer, but aligned on 1MB boundary, I have no real way to ask this from the OS. So this is implemented directly by over-allocating. To be fair, I can’t think of a good reason why you’ll want to do something like that (you have no guarantees about what the <em>actual physical </em>memory layout would be after all, and that is the only scenario I can think this would be useful. Given that page aligned memory (which is what you get anyway from the OS) is usually sufficient, I wonder what is the use case for that here.</p><p>I get why mimalloc have to struggle with this, given that it is limited to just returning a pointer back (malloc interface), and doesn’t have a good way to tell that it played games with the alignment when you pass a value to <em>free()</em>. There seems to be a lot of code around here to deal with memory alignment requirements.&nbsp; I think that I’ll need to go up the stack to figure out what kind of alignment requirements it has.</p><p>That is enough for now, I think. I’m going to get to the core of mimalloc in the next post.</p>https://www.ayende.com/blog/188001-C/reviewing-mimalloc-part-ii?Key=85573868-a09c-45e6-9f1e-044d7da4a799https://www.ayende.com/blog/188001-C/reviewing-mimalloc-part-ii?Key=85573868-a09c-45e6-9f1e-044d7da4a799Mon, 22 Jul 2019 12:00:00 GMTReviewing Sled: Part III<p>Unusually for me, I had a bit of a pause in reviewing <a href="https://github.com/spacejam/sled/">Sled</a>. As a reminder, Sled is an embedded database engine written in Rust. I last stopped looking at the buffer management, but I still don’t really have a good grasp of what is going on.</p><p>The next file is the iterator. It looks like it translates between segments and messages in these segments. Here is the struct:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_2.png"><img width="1152" height="305" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_thumb.png" border="0"></a></p><p>As you can see, the log iterator holds an iterator of segments, and iterating over it looks like it will go over all the messages in the segments in order. Yep, here is the actual work being done:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_4.png"><img width="839" height="154" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_thumb_1.png" border="0"></a></p><p>The <em>next()</em> method is fairly straightforward, I found. But I have to point out this:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_6.png"><img width="961" height="159" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_thumb_2.png" border="0"></a></p><p>First, the <em>will need</em> call is really interesting. Mostly because you have a pretty obvious way to do conditional compiling that doesn’t really sucks. <em>#if</em> is usually much more jarring in the code.</p><p>Second, I think that the style of putting really important functions inside an if result in a pretty dense code. Especially since the if is entered only on error. I would have preferred to have it as a stand alone variable, and then check if it failed. </p><p>What I don’t understand is the <em>read_segment</em> call. Inside that method, we have:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_8.png"><img width="916" height="81" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_thumb_3.png" border="0"></a></p><p>There are also similar calls on segment trailer. It looks like we have a single file for the data, but stuff that is too large is held externally, in the blob files. </p><p>We then get to this guy, which I find really elegant way to handle all the different states.</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_10.png"><img width="926" height="432" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_thumb_4.png" border="0"></a></p><p>That is about it for interesting bits in the iterator, the next fun bit is the Log. I do have to admit that I don’t like the <em>term</em> log. It is too easy to get it confused with a debug log. In Voron, I used the term Journal or Write Ahead Journal (OH in the office: “did we waj it yet?”). </p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_12.png"><img width="1020" height="445" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_thumb_5.png" border="0"></a></p><p>The fact that you need to figure out where to get the offset of the data you are about to write is really interesting. This is the method that does the bulk of the work:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_14.png"><img width="1083" height="299" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_thumb_6.png" border="0"></a></p><p>Note that it just reserve and complete the operation. This also does <em>not</em> flush the data to disk. That is handled by the flusher or by explicit call. The <em>reserve()</em> method calls to <em>reserve_internal()</em> and there we find this gem:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_16.png"><img width="1197" height="525" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_thumb_7.png" border="0"></a></p><p>I know what it does (conditional compilation), but I find it really hard to follow. Especially because it <em>looks </em>like a mistake, with <em>buf </em>being defined twice. This is actually a case where an <em>#if</em> statement would be better, in my eyes.</p><p>Most of the code in there is to manage calls to the iobuf, which I already reviewed. So I’m going to skip ahead and look at something that is going to be more interesting, the page cache. Sled has an interesting behavior, in that it can shred a page into multiple location, requiring some logic to bring it all back together. That is going to be really interesting to look at, I hope.</p><p>The file stats with this:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_18.png"><img width="1301" height="416" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_thumb_8.png" border="0"></a></p><p>And this… takes a while to unpack.&nbsp; Remember that epoch is manual GC pattern for concurrent data structure without GC.</p><p>The <em>cached_ptr</em> value is a shared pointer to a Node (inside a lock free stack) that holds a CacheEntry with static lifetime and thread safe to a generic argument that must have static lifetime and be thread safe. And there is a unsigned long there as well.</p><p>No idea yet what is going on. But here is the first method on this struct:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_20.png"><img width="901" height="145" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_thumb_9.png" border="0"></a></p><p>That is… a lot. The cache entry is a discriminated union with the following options:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_22.png"><img width="723" height="376" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_thumb_10.png" border="0"></a></p><p>There are some awesome documentation comments here, including full blown sample code that really help understand what is going on in the code.</p><p>There seems to be a few key methods that are involved here:</p><ul><li>allocate(val) – create a new page and write an initial value, gets back a page id.</li><li>link(id, val) – make a write to a page id. Which simply write a value out.</li><li>get(id) – read all the values for a page id, and uses a materializer to merge them all to a single value.</li><li>replace(id, val) – set the page id to the new value, removing all the other values it had.</li></ul><p>The idea here, as I gather. Is to allow sequential writes to the data, but allow fast reads, mostly by utilizing SSD’s random read feature.</p><p>I’m trying to follow the code, but it is a bit complicated. In particular, we have:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_24.png"><img width="682" height="189" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-Sled-Part-III_11EA8/image_thumb_11.png" border="0"></a></p><p>This try to allocate either a free page or allocate a new one. One of the things that really mess with me here is that the use of the term Page. I’m using to B+Tree, where a page is literally some section of memory. Here it refers to something more nebulous. Key point here, I don’t see where the <em>size</em> is specified. But given that you can link items to page, that sort of make sense. I just need to get used to Pages != storage. </p><p>The fact that all of this is generic also make it hard to follow what is actually going on.&nbsp; I’m getting lost in here, so I think that I’ll stop for now.</p>https://www.ayende.com/blog/187073-C/reviewing-sled-part-iii?Key=8e4c8a31-646a-4c58-a99c-83a7d67beb12https://www.ayende.com/blog/187073-C/reviewing-sled-part-iii?Key=8e4c8a31-646a-4c58-a99c-83a7d67beb12Tue, 23 Apr 2019 12:00:00 GMTReviewing FASTER: Summary<p><a href="http://github.com/microsoft/FASTER/">FASTER</a> is an interesting project, with some unique approaches to solving their tasks that I haven’t encountered before. When I initially read the paper about a year or so ago, I was impressed with what they were doing, even I didn’t quite grasp exactly what was going on. After reading the code, this is now much clearer. I don’t remember where I read it, but I remember reading a Googler talking about the difference between Microsoft and Google with regards to publishing technical papers. Google would only publish something after it has been in production for a while (and probably ready to sunset <img class="wlEmoticon wlEmoticon-smile" style="" alt="Smile" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Summary_66D/wlEmoticon-smile_2.png">) while Microsoft would publish papers about software that hasn’t been deployed yet. </p><p>The reason I mention this is that FASTER isn’t suitable for production. Not by a long shot. I’m talking about issues such as swallowing errors, writing to the console as an error handling approach, calling sleep(), lack of logging / tracing / visibility into what is going on in the system. In short, FASTER looks like it was produced to support the paper. It is proof of concept / research code, not something that can take and use. </p><p>You can see it clearly in the way that the system is designed to be operated. You have dedicated threads that process requests as fast as they possibly can. However, there is no concept of working in any kind of operational environment, you can’t start using FASTER from an ASP.Net MVC app, for example. They models are just too different. I can think of a few ways to build a server using the FASTER model, but they are all pretty awkward and very specialized. This lead to the next issue with the project, it is <em>highly</em> specialized solution.</p><p>It isn’t meant for general consumption. In fact, as I can figure out, this is perfect if you have a relatively small working set that you do a <em>lot</em> of operations on. The examples I have seen given are related to tracking ads, which is a great example. If you want to store impressions on an ad, the active ads are going to pretty small, but you are going to have a lot of impressions on them. For other stuff, I don’t see a lot of usage scenarios.</p><p>FASTER is limited in the following ways:</p><ul><li>Get / Set / Update only – no way to <em>query</em></li><li>No support for atomic operations on more than a single key</li><li>Can support fixed length values only</li><li>Crash means data loss (of the most recent 14.4 GB, usually)</li><li>The API is <em>awkward</em> to use, and you need to write a bit of (non trivial) code for each key/val you want to store.</li><li>No support for compaction of data beyond dropping the oldest entries</li></ul><p>Some of these issues can be mitigated. For example, compaction can be implemented, and you can force data to be written to disk faster if you want to, but those aren’t in the box and require careful handling.</p><p>Now that I have gone over the code, I’m not quite sure what was the point there, to be honest. In terms of performance, you can get about 25% of the achieved performance by just using ConcurrentDictionary in .NET, I’m pretty sure that you can do better by literally just using a concurrent hash map in C++. This isn’t something that you can use as a primary data store, after all, so I wonder why not just keep all the data in memory and be done with it. </p><p>I liked the mutable / read only portions of the log, that is certainly a really nice way to do it, and I’m sure that the epoch idea simplified things during the implementation with the ability to not worry about concurrent accesses. However, the code is <em>complex</em> and I’m pretty sure that it is <em>not</em> going to be fun to debug / work with in real world scenarios.</p><p>To sum it up, interesting codebase and approaches, but I would caution from using it for real. The perf numbers are to salivate over,&nbsp; but the manner in which the benchmark was written means that it is not really applicable for any real world scenario.</p>https://www.ayende.com/blog/184387-A/reviewing-faster-summary?Key=e3ebba51-faaf-4961-8bc6-86015458a43dhttps://www.ayende.com/blog/184387-A/reviewing-faster-summary?Key=e3ebba51-faaf-4961-8bc6-86015458a43dThu, 06 Sep 2018 09:00:00 GMTReviewing FASTER: When the data hits the disk<p>So far, I ignored anything in <a href="https://github.com/Microsoft/FASTER/">FASTER</a> about how the data actually hits the disk. Based on my reading of the code and the paper, here is what I <em>think</em> that is going on. FASTER works in segments and in conjunction with its allocator. When you create a new instance, you have to define what would be the log size. From looking at the code, they seem to be favoring 16GB as the default size of the log. This is passed to PersistentMemoryMalloc, which uses pages of 32MB each to manage the memory. Out of these 16GB, 14.4GB are considered to be mutable and 1.6 GB is considered to be read only. </p><p>On startup, this class allocates two pages (64MB). Whenever FASTER needs more memory, such as to store a new record, it will eventually call to this method:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Writing-data_142D/image_2.png"><img width="910" height="517" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Writing-data_142D/image_thumb.png" border="0"></a></p><p>Again we have <em>num_slots</em> that actually means size in bytes, but I’ll leave my pet peeves aside. </p><p>You can see that this allocates from tail of the page use <em>Reserve, </em>which does an atomic operation. If we run out of space in the 32MB page, the <em>caller</em> need to call <em>NewPage()</em> to handle the new allocation. This plays together with the buffer management and the epoch. In particular, here is how a new page is allocated in the normal case. Assuming we just started and we consumed 64MB of memory, the new entry will allocate the 3rd page. This will also move the section of read only memory when there is a new page allocated and the number of active pages is beyond 14.4 GB.</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Writing-data_142D/image_4.png"><img width="1158" height="361" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Writing-data_142D/image_thumb_1.png" border="0"></a></p><p>What this means, in practice, is that FASTER typically has a 14.4GB range in which all operations are working on purely mutable memory. That means that two impressions on the same ad will end up being very close to simply Interlocked.Increment on the relevant value. This is the key for the performance that FASTER exhibits. </p><p>What happens once we start going beyond the 14.4 GB? FASTER will begin to move data to the read only section. In this case, it means that the any new modifications to the data will create a new copy of it in the mutable section.</p><p>At the same time, <em>PageAlignedShiftReadOnlyAddress()</em> will also starts an async process of writing these readonly pages to disk. Here is how it works:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Writing-data_142D/image_8.png"><img width="1241" height="277" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Writing-data_142D/image_thumb_3.png" border="0"></a></p><p>If the <em>read_only_address</em> was updated, FASTER calls to <em>BumpCurrentEpoch()</em> and will execute <em>OnPagesMarkedReadOnly</em>() when the Epoch moves beyond this range (this works because then it is guaranteed that no one may mutate this memory).&nbsp; That method looks like this:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Writing-data_142D/image_10.png"><img width="1167" height="339" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Writing-data_142D/image_thumb_4.png" border="0"></a></p><p>The notion of <em>read_only_address</em> and <em>safe_readonly_only_address</em> is discussed in the paper quite nicely, by the way. <em>AsyncFlushPages()</em> writes the data to disk, as you would expect and updates various in memory structures.</p><p>Note that just having the data written to disk doesn’t remove the in memory copy. It is still available in memory until it is pushed back from the log by new data. Now that we understand how the data goes to the log and then to the disk, I want to figure out how the data is written in to the disk itself. Actual writes are handled here:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Writing-data_142D/image_12.png"><img width="973" height="519" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Writing-data_142D/image_thumb_5.png" border="0"></a></p><p>What you can see is that the destination offset is used to divide the data on disk to sections. Each section is 1GB in size. In other words, the way FASTER works is to write the data in 1 GB segments that are sequential over time. </p><p>This also plays into the expiration policy that FASTER employs. Because it uses a logs based system, it accumulate data over time and will need to handle that. The current way it deals with the problem is to just delete old files, this gets rid of the data in roughly time based order, which is suitable for the use case that the paper talks about. Another alternative is to read the old files, and move the still valid entries to the start. That doesn’t seem to be implemented and I think it will be pretty hard to do and likely consume a <em>lot</em> of resources.</p><p>I’ll probably have another summary post about FASTER, but that is pretty much it. I’ve ignored other parts (recovery, several state machines used to handle internal state, etc), but they aren’t important to grokking what it is actually doing. It is an interesting codebase, but it feels… incomplete. But I’ll reserve such thoughts to the summary post.</p>https://www.ayende.com/blog/184386-A/reviewing-faster-when-the-data-hits-the-disk?Key=04032ab3-2f56-40f3-b546-5c76296372fchttps://www.ayende.com/blog/184386-A/reviewing-faster-when-the-data-hits-the-disk?Key=04032ab3-2f56-40f3-b546-5c76296372fcWed, 05 Sep 2018 09:00:00 GMTReviewing FASTER: Reading data from disk<p>One of the things that <a href="https://github.com/microsoft/FASTER/">FASTER</a> claims is that it is suitable for larger than memory datasets with its hybrid log approach. I briefly went over that code during my review, but I want to dedicate this post to discussing how FASTER work with the disk.</p><p>FASTER is using async I/O on Linux &amp; Windows to do its I/O, which present its own challenges, in particular, how do you handle an operation that is going to need to hit the disk ( to read old data). Another thing that I would like to discover is how does it save the data to disk. We’ll start from the reading data part.</p><p>Reading in FASTER looks like this:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_8.png"><img width="794" height="161" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_thumb_3.png" border="0"></a></p><p>You pass a context, a callback and serial number. I’m not sure what the serial is about, I <em>think</em> it is about checkpoints, but not sure. You’ll be called with the results once the operation executed. </p><p>Here is the core of the Read method:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_6.png"><img width="1219" height="359" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_thumb_2.png" border="0"></a></p><p>FASTER first checks the in memory details, and if it isn’t there it is either not found or on disk. This is actually really interesting, because it implies that FASTER keep track of all of the data items purely in memory. Let’s go to InternalRead and figure out how that works. We already looked into most of that in the <a href="https://ayende.com/blog/184354-A/reviewing-faster-the-hash-structure">previous post</a> FindEntry is called to find the entry by it’s hash. FASTER keep all the hashes in memory, even while it is writing entries to disk. This way, it can easily answer if an entry exists or not. Note that as I read things, if FASTER has more than a certain number of values, hash collision rate is going to reach high percentage, requiring often expensive I/O to figure out whatever the value exists.</p><p>If the value is in memory, your ReadContext.GetAtomic() method will be called. Here is one such implementation:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_10.png"><img width="626" height="93" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_thumb_4.png" border="0"></a></p><p>Where the value was defined as:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_12.png"><img width="622" height="127" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_thumb_5.png" border="0"></a></p><p>If the value has already been moved to the read only section, it will use the Get() method, instead, as an optimization. If the value is not on the mutable section or the read only section, it is on the disk, in which case we have this code:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_14.png"><img width="1188" height="162" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_thumb_6.png" border="0"></a>&nbsp;</p><p>The <em>go_async()</em> method just records the status of the operation when we started the async process, it doesn’t actually invoke anything async. That is done in the caller code, Read().</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_16.png"><img width="1222" height="155" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_thumb_7.png" border="0"></a></p><p>This, in turn, gets us to this piece of code:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_18.png"><img width="810" height="150" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_thumb_8.png" border="0"></a></p><p>Note that the code is <em>full</em> of handling of the current phase of the thread. I’m ignoring all of these for now, they aren’t important. </p><p>The next thing to look at is the async I/O request, which gets us to:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_20.png"><img width="1240" height="397" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_thumb_9.png" border="0"></a></p><p>We first register the pending I/O, then actually starts to process the async call. Except that not really. <em>AsyncGetFromDisk()</em> isn’t actually doing I/O.&nbsp; Instead, it seems to be focused on limiting the number of concurrent I/O operations that are going on.</p><p>In this case, if there are more than 120 pending I/Os, it will call <em>io_getevents()</em> in Linux and <em>GetQueuedCompletionStatus()</em> and try to process any completed I/O immediately. </p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_22.png"><img width="1101" height="429" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_thumb_10.png" border="0"></a></p><p>ProtectAndDrain is more interesting. It asks the epoch to complete any pending operations. Such actions are the creation of a new segment file or the moving of a segment from memory to disk.</p><p>I find it interesting that FASTER chose to stall the thread until all pending I/Os are completed. Given their model, it would make more sense to push such operation into the thread ops and resume process other requests. I guess that they expect this to be either a rare case or using this for throttling overall system load. You might also have noticed the num_records arguments, this is used in the hlog method:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_24.png"><img width="629" height="37" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_thumb_11.png" border="0"></a></p><p>That was <em>confusing</em>, I expected this to be the number of records (as in, how many records are read from disk) but this is the number of bytes to read.</p><p>The C++ memory model make async code a bit complex. In particular, if you’ll look at the first code snippet in this post, you’ll see that we pass a stack allocated struct to the Read() method. Because this method can complete in an async manner, what FASTER will do is to perform a deep copy of the data. Combine that with lambda’s capturing state, and I’m pretty sure that this code will cause a <em>very</em> subtle memory corruption in rare cases.</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_28.png"><img width="841" height="220" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_thumb_13.png" border="0"></a></p><p>What I <em>think</em> will happen is that we capture by ref the stack variable and in 99% of the cases, we’ll run this in sync mode, meaning that there will be no issues. Only if the value needs to be read from disk will you get an issue. Because that function will already return but you still have the callback (and the captured reference now pointing to something completely different) still active. I think that a C++ developer would recognize this, and the fact that C++ require you to be explicit about your captures make this a lot easier to deal with. </p><p>It is important to note that there is no good way to actually handle the async nature of certain operations here. Any code that actually handle the operation need to go in the callback. </p><p>Another aspect to remember is that just reading from the disk doesn’t mean that you got the right value. You might have gotten a hash collision:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_30.png"><img width="1023" height="449" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_2B/image_thumb_14.png" border="0"></a></p><p>In other words, if you have a way to generate hash collisions, as soon as the value hits the disk, you are going to be facing with making N <em>disk I/O requests</em> to find if you have the value or not.&nbsp; <a href="https://news.ycombinator.com/item?id=3401900">Denial of service attacks against hash tables</a> are well known and represent a significant risk of to services. </p><p>Next on my list if seeing how FASTER is actually <em>writing </em>the data to disk, but that will be in a separate post. </p>https://www.ayende.com/blog/184385-A/reviewing-faster-reading-data-from-disk?Key=bbe0732a-22ff-479b-bad6-4c1fe0a558d4https://www.ayende.com/blog/184385-A/reviewing-faster-reading-data-from-disk?Key=bbe0732a-22ff-479b-bad6-4c1fe0a558d4Tue, 04 Sep 2018 09:00:00 GMTReviewing FASTER: The hash structure<p>Continuing my stroll through the FASTER codebase, I want to try to get to the core stuff, not just the periphery. This is a bit hard, because there is a lot of code here and it seems to be hard to find where the real action is happening. I decided to find how FASTER is handling Get and Put operations.&nbsp;&nbsp; There is something called InternalHashTable inside FasterKv, which seems promising,&nbsp; so I’m going to follow up on that. Interestingly enough, it shows up as:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_118CC/image_2.png"><img width="1138" height="75" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_118CC/image_thumb.png" border="0"></a></p><p>So there are some funky stuff here to consider too, but we’ll deal with that later, for now, I want to understand what it is doing with the InternalHashTable. Inside that class, there is the notion of bukcets:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_118CC/image_6.png"><img width="914" height="67" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_118CC/image_thumb_2.png" border="0"></a></p><p>And a bucket is:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_118CC/image_8.png"><img width="927" height="410" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_118CC/image_thumb_3.png" border="0"></a></p><p>This starts to get interesting for me, so let’s dig deeper into HashBucketEntry, where they use the same union trick I talked about in <a href="https://ayende.com/blog/184321-A/reviewing-faster-digging-into-the-c-impl">my previous posts</a>, this allow to easily define an atomic version of it with very little effort.</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_118CC/image_10.png"><img width="884" height="274" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_118CC/image_thumb_4.png" border="0"></a></p><p>There is also the overflow entry definition, which looks like this:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_118CC/image_12.png"><img width="424" height="213" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_118CC/image_thumb_5.png" border="0"></a></p><p>I got to say, I’m really liking this approach for handling data packing as well as multi threading concerns. It also plays very well with the actual cache line architecture of modern CPUs.</p><p>There is also the KeyHash struct, whose heart is this:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_118CC/image_14.png"><img width="423" height="244" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_118CC/image_thumb_6.png" border="0"></a></p><p>So far, this lines us very neatly to how the FASTER paper talks about things. Given a KeyHash, here is how you get it’s bucket:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_118CC/image_16.png"><img width="574" height="97" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_118CC/image_thumb_7.png" border="0"></a></p><p>This simply index into the <em>buckets_</em> array by taking the (size_<em> –1)</em> bits from the hash itself. That tells me that the FASTER structure is <em>very</em> sensitive to the choice of hash function that will be used. This <a href="https://softwareengineering.stackexchange.com/questions/49550/which-hashing-algorithm-is-best-for-uniqueness-and-speed">SO answer</a> has some amazing detail on analysis of the output of hash functions with different outputs, which can give you some idea about why this matters so much. <a href="https://probablydance.com/2018/05/28/a-new-fast-hash-table-in-response-to-googles-new-fast-hash-table/">This post</a> is an <em>in depth</em> discussion of this, as well of why the choice of hash function <em>matters</em>. This is enough for me to stop everything and try to find what kind of hash function is actually being used here.</p><p>The user get to define their own hash function, and if they do so badly (for example, use the identity function since they have an 8 bytes value and they need an 8 bytes hash) that is going to be fun. The function that they seem to be using in their examples is this one:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_118CC/image_18.png"><img width="1052" height="307" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_118CC/image_thumb_8.png" border="0"></a></p><p>A bit of searching on the interweb didn’t narrow it down to something specific, it may be something that they wrote specifically for that. Given that the paper doesn’t mention this, it doesn’t seem to be something special.</p><p>Given that 40343 is a prime, it seems like a pretty common pattern of multiple by a prime with each 16 bits portion of the key. The idea is that the multiplication by prime will spread the bits around. No idea how high the quality of this hash function is, since actual analysis would take at least a few hours. At least at a glance,&nbsp; it doesn’t seem to be awful, but I do wonder whatever something like FNV-1. In fact, this is very similar, but with different prime and offset and with addition instead of XOR. </p><p>The actual InternalHashTable class doesn’t actually <em>do</em> something, there are a few functions around checkpointing and recovery, but they aren’t very interesting at this point. I’m going back to actually working with the has table and looked at how reads work. They end up in the InternalRead method which does the majority of the work inside the FindEntry function. The first thing that happens <em>there</em> is:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_118CC/image_20.png"><img width="1057" height="100" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_118CC/image_thumb_9.png" border="0"></a></p><p>The first line is there to handle growing the hash table on the fly and will be ignored for now. As you can see, this basically just gives me the relevant bucket. Here is the next stage, once we <em>have</em> a bucket, we need to find the relevant entry that matches what we are looking for. Here is the code:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_118CC/image_22.png"><img width="1189" height="823" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_118CC/image_thumb_10.png" border="0"></a></p><p>You can see that there are a bunch of stuff going on here. First, we run over the known entries in the bucket, trying to find an entry with the same tag. You can see the <em>tentative</em> usage, which is used to sync up between concurrent readers and writers. There may be more items in the bucket than we have space for, so there is also the concept of overflow. This is basically a linked list of 7 items at a time and likely to be pretty fast (frequently already in the higher tiers of the cache for commonly accessed data). </p><p>Now that we have the entry, let’s see what is done with it. I’m currently reading through the InternalRead code. Just finding the matching hash doesn’t really help us, we may have hash collisions, this is handled here:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_118CC/image_24.png"><img width="877" height="523" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_118CC/image_thumb_11.png" border="0"></a></p><p>This is the first time that we actually run into the hybrid log (<em>hlog</em> here). But the basic idea is pretty obvious. Either the key match, or we have a pointer to the previous entry. I’m not seeing any handling of complete mismatch, though. I’m pretty sure that <a href="https://github.com/Microsoft/FASTER/issues/26">this is a bug</a> (the behavior is different in the C# version).</p><p>This is enough for now, from here the InternalRead code is starting to do things with the hlog, state machine, etc. I’m going to handle that in a separate post.</p>https://www.ayende.com/blog/184354-A/reviewing-faster-the-hash-structure?Key=916292d2-ed28-4908-b12e-189516ae0556https://www.ayende.com/blog/184354-A/reviewing-faster-the-hash-structure?Key=916292d2-ed28-4908-b12e-189516ae0556Mon, 03 Sep 2018 09:00:00 GMTReviewing FASTER: Working with the file system<p>In my <a href="https://ayende.com/blog/184321-A/reviewing-faster-digging-into-the-c-impl?key=7c5bfc3488fc4ef1b4f17f3d49cad989">last post</a> I dove into the Epoch implementation. The Epoch is explained very nicely in the paper, and the code follows the paper pretty closely. The code make sense, but I still lack the proper… feeling for how it is actually being used. The Epoch allows you to register code that will be executed when the epoch is updated, which is the key to how FASTER is making progress, but while I can see that this is being called from the allocators, I haven’t really grokked it yet. I’m going to back to the faster.h file and see what I can glean from there.</p><p>Because of the template utilization, it is kinda hard to figure out what exactly is going on, I’m going to look at some of the examples and try to figure out what it is doing there. Here is one instance of this class:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_2.png"><img width="1275" height="101" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_thumb.png" border="0"></a></p><p>AdId and NumClicks are just two ways to provide operations on 8 bytes keys and values. I like these examples because they provide good use case to talk about FASTER usage.</p><p>This code leads me to the FileSystemDisk, which is defined as:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_4.png"><img width="594" height="214" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_thumb_1.png" border="0"></a></p><p>In the FileSystemFile, we have this code:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_6.png"><img width="1505" height="352" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_thumb_2.png" border="0"></a></p><p>This is pretty simple, but I was quite amused by this, because this is C# API sneaking up again in the C++ code. There is also this:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_8.png"><img width="746" height="125" title="image" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_thumb_3.png" border="0"></a></p><p>I’m not sure what this bundle is, though. I run into this code in the class:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_10.png"><img width="887" height="287" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_thumb_4.png" border="0"></a></p><p>This is… not nice, in my eyes. Based on this code, whoever allocated this instance also allocated a buffer large enough to include more data there. This is fairly common, since you want to work with such data together, but I find it ugly / risky because it means that there are multiple locations that needs to be aware of it. I would like it better if they just passed the pointer explicitly. That would avoid this snippet:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_12.png"><img width="1536" height="250" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_thumb_5.png" border="0"></a></p><p>Which I find pretty annoying to read. What is stranger is that to use this, you have to write (bundle_t has been typedef for the FileSystemSegmentBundle):</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_14.png"><img width="1544" height="131" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_thumb_6.png" border="0"></a></p><p>I get what is going on here, but I just find it really awkward to handle. There are multiple places where you need knowledge of this allocation pattern and I don’t believe that the benefit of placing all of the data together is <em>that</em> important. For that matter, given the importance of <em>not</em> using new explicitly in modern C++, I’m sure that there are other ways to achieve the same goal that would be more natural.</p><p>Going through the code, we now have:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_16.png"><img width="620" height="90" title="image" style="border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_thumb_7.png" border="0"></a></p><p>I decided to make this post about the file system usage, because there is a <em>lot</em> of pretty complex code here that I would like to understand. I finally figured out what the S is, this is the segment size:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_18.png"><img width="1186" height="98" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_thumb_8.png" border="0"></a></p><p>This means that the earlier definition of FasterKv basically defined Segment Size of 1 GB in size. I’m not sure what these segments <em>are</em>, though. I’m pretty sure that this is how they manage time base expiration, but I’m not certain. Following upward from the creation of a new segment, we have WriteAsync, like so:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_20.png"><img width="1352" height="709" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_thumb_9.png" border="0"></a></p><p>You can see that the segment number is basically just the file id, and if the file does not already exists, we call OpenSegment on it. Afterward, we call WriteAsync on that specific file. I’ll look into how that work in a bit, this isn’t that interesting at the moment. Right now I want to dig into OpenSegment. I removed some error handling here, but the gist of it is clear. </p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_24.png"><img width="1589" height="1082" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_thumb_11.png" border="0"></a></p><p>The actual code also handles threading and errors, which I omitted. You can see that it creates the new files, copying them from the existing value. Then it creates a context that holds the <em>old</em> files and pass it to BumpCurrentEpoch.</p><p>When FASTER is sure that no one else is looking at this epoch, it will call the callback and delete / dispose the old files. This is a nice way to ensure consistency. LMDB does something similar with its transactions’ table. So now we know that whenever we write at a 1GB boundary, FASTER will generate a new epoch. </p><p>What about the actual writing? Here is what this looks like (the Linux impl):</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_26.png"><img width="1523" height="433" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_thumb_12.png" border="0"></a></p><p>On Linux, this ends up being:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_28.png"><img width="937" height="59" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_thumb_13.png" border="0"></a></p><p>This is then checked in TryComplete:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_30.png"><img width="1262" height="123" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_91F3/image_thumb_14.png" border="0"></a></p><p>This is called FasterKv.CompletePending(), which seems to be called occasionally by FASTER. On Windows, this is using async I/O and callbacks to handle this.</p><p>Okay, this is already long enough, but I got a handle on how FASTER is writing to disk, even though I don’t know yet what it is <em>doing</em> with that. I also saw an actual use of Epoch that made sense (clearing old data once no one is looking at that).</p>https://www.ayende.com/blog/184353-A/reviewing-faster-working-with-the-file-system?Key=6f5d1bdf-7164-4b10-ab17-cfb7a808d696https://www.ayende.com/blog/184353-A/reviewing-faster-working-with-the-file-system?Key=6f5d1bdf-7164-4b10-ab17-cfb7a808d696Fri, 31 Aug 2018 09:00:00 GMTReviewing FASTER: Digging into the C++ impl<p>After going over the <a href="https://ayende.com/blog/184225-A/reviewing-faster-reading-the-paper?key=ced524c829bc4a1fae86a3c7facc325b">paper</a> and the <a href="https://ayende.com/blog/184257-A/reviewing-faster-lets-start-with-managed-code?key=478acffa3cca45daaf9de4c00a6d378d">managed implementation</a>, I’m ready to start with the C++ implementation. I have higher hopes for this code. As I started browsing the C++ code, it occurred to me that the way the C#’s implementation handles dynamic code generation is pretty much how templates in C++ work. I wonder if this was the trigger for that.</p><p>The C++ code reads a lot more naturally to me. There are some nice tricks that are used there that are a joy to read. For example, take a look at Address manipulation:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_4.png"><img width="825" height="276" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_thumb_1.png" border="0"></a></p><p>The colon syntax are a way to express bitfields in C. But the real fun part for me is the idea of <em>control_</em>. What is this for? Well, it runs out that in addition to Address, the also defined AtomicAddress, whose implementation need to provide atomic operation on address. This is implemented in the following manner:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_6.png"><img width="1193" height="732" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_thumb_2.png" border="0"></a></p><p>I find this a really elegant way to handle this requirement.</p><p>Another amusing observation is that almost all the code in FASTER is in .h files, because of the heavy use of templates. I wonder how that affects compilation speed and how that would play in larger projects.</p><p>It is in <em>faster.h</em> that we start to get into the interesting bits. I first run into this guy:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_10.png"><img width="758" height="431" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_thumb_4.png" border="0"></a></p><p>This maps pretty closely to what I have actually seen the C# code does, but in C++ it is a much more natural approach that dynamic compilation on the fly as it did in C#. </p><p>Next we have the constructor, which looks like this:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_12.png"><img width="1105" height="544" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_thumb_5.png" border="0"></a></p><p>The <em>epoch_</em> field is auto initialized by the compiler and is not shown here. This indicates that FASTER can handle up to 2.1 billion entries in total, which seems to be a strange limit for a data store that is expected to handle hundreds of thousands of operations per second. I’m going to jump around the codebase a bit, because I want to follow exactly what is going on when initializing this class. The first place to look is the epoch. The idea of epoch is described in the paper, so I’m not going to repeat it. The code defines a struct that is 64 bytes in size (cache line sized, to avoid false sharing), this is used to store a thread specific value and is used to maintain most of the invariants of the epoch.</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_16.png"><img width="702" height="357" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_thumb_7.png" border="0"></a></p><p>When switching between epochs, there are actions that needs to be run, here is what this looks like in the code. </p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_14.png"><img width="1002" height="663" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_thumb_6.png" border="0"></a></p><p>I must say, this really mess up with my mind, because we have C#’s naming conventions (TryPop, TryPush) in C++ code. It’s like the code couldn’t decide what shape it wanted to be in <em>either</em> language.</p><p>The number of threads that can take part is limited by this value:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_18.png"><img width="879" height="62" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_thumb_8.png" border="0"></a></p><p>Right now, this is set to 96, which means that if you need more threads than that, you’ll get a runtime error. This fits nicely with the model FASTER uses of long running threads, but I don’t see how it can play well with actually accepting commands from network / other location. </p><p>As part of it’s constructor, this method is called, which actually does the real work of setting up the epoch.</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_20.png"><img width="1072" height="393" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_thumb_9.png" border="0"></a></p><p>I’m not really sure at this point why it is allocating two additional entries beyond the specified size. </p><p>When a thread start running FATER code, it needs to register itself with the Epoch, this is done in the <em>Protect()</em> call. </p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_22.png"><img width="832" height="151" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_thumb_10.png" border="0"></a></p><p>Going into the <em>Thread</em> class reveals a simple table of values that are used to give ids to the threads that asked to get an id. This is done in this function:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_24.png"><img width="1096" height="342" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_thumb_11.png" border="0"></a></p><p>It took me a couple of times of reading the first two lines to understand what is going on here. This is an <em>awesome</em> way to handle a circular buffer scanning. It is very clear and saves a bunch of code ( at the cost of doing mod operation, which can be usually be masked if the value is known at compile time and is a power of 2, which in this case it is not). I’m probably going to use this the next time I need to implement scanning through a ring buffer. </p><p>Then we have computing the earliest safe epoch:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_26.png"><img width="1255" height="560" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_thumb_12.png" border="0"></a></p><p>The first of these methods is elegant, it does a simple read from the table, reading potentially stale values. This doesn’t matter, because the worst thing that can happen is that we’ll keep a previous epoch for longer than it is required. </p><p>The second one reads wrong to me, but I’ll have to dig deeper into the C++ memory model more deeply for this. The problem is that this seems like it is relying on the CPU to update its state somehow. But I don’t see any instruction that would force it to. I <em>think</em> that the set to <em>safe_to_reclaim_epoch</em> (which is std::atomic&lt;uint64_t&gt;) will use memory_order_seq_cst for the operation, but I’m not sure how that would impact reads from the <em>table_</em>. </p><p>Also, I want you to pay attention to the variable names here. Private member fields:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_28.png"><img width="519" height="124" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_thumb_13.png" border="0"></a></p><p>Public member fields:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_30.png"><img width="697" height="155" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Digging-into-the-C-impl_AF0F/image_thumb_14.png" border="0"></a></p><p>And then we have SpinWaitForSafeToReclaim that uses:</p><ul><li>safe_to_reclaim_epoch – public member field</li><li>safe_to_reclaim_epoch_ – method argument </li></ul><p>I’m not sure if this a common practice in C++, but this is <em>really </em>confusing to me. This is enough for now, I’m going to keep going thought the C++ code in my next post. There hasn’t been anything really interesting so far, just digging into the code and getting a feel as to how it is put together.</p>https://www.ayende.com/blog/184321-A/reviewing-faster-digging-into-the-c-impl?Key=7c5bfc34-88fc-4ef1-b4f1-7f3d49cad989https://www.ayende.com/blog/184321-A/reviewing-faster-digging-into-the-c-impl?Key=7c5bfc34-88fc-4ef1-b4f1-7f3d49cad989Thu, 30 Aug 2018 09:00:00 GMTReviewing FASTER: Let’s check these numbers<p>Before heading to the C++ implementation, I thought I would take the time to just walk through the FASTER codebase as it is performing a simple operation. The access violation error that I previously run into has been fixed, so I could run the benchmark. Here is my configuration:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-How-does-this-thing-wor_9EA4/image_2.png"><img width="671" height="38" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-How-does-this-thing-wor_9EA4/image_thumb.png" border="0"></a></p><p>I got the following results, when running on a single thread.</p><blockquote><p>Total 142,603,719 ops done&nbsp; in 30 secs.</p></blockquote><p>This is about 4.7 million operations per second, which is certainly nice. I then decided to compare this to ConcurrentDictionary to see what kind of performance that would give me. I made the <a href="https://gist.github.com/ayende/785cfa6107332a8c9c2b203d30bf8e7e">following changes</a>, which I’ll admit are pretty brute force way to go about it. But note that this is probably significantly less efficient then I could probably write it. Nevertheless, using ConcurrentDictionary with a single thread in the same benchmark gives me:</p><blockquote><p>Total 84,729,062 ops done&nbsp; in 30 secs.</p></blockquote><p>There isn’t much call for using this on a single thread, though. My machine has 20 cores, so let’s see what happens when I give FASTER its head, shall we?</p><blockquote><p>2,330,054,219 ops done&nbsp; in 30.021 secs.</p></blockquote><p>That is impressive, with about 77,668,473 operations per second. On the other hand, this is what happened when I run with 20 threads and ConcurrentDictionary:</p><blockquote><p>671,071,685 ops done&nbsp; in 30.024 secs.</p></blockquote><p>This gives us “only” 22,369,056 operations per second. </p><p>It is clear that FASTER is much better, right? The problem is that it isn’t much faster <em>enough</em>. What do I mean by this? I used idiomatic C# for the ConcurrentDictionary usage and got with 1/4 of FASTER’s perf. The FATER codebase is doing native calls and unsafe manipulation, dedicated allocation, etc. I expect to get better perf at that point, but “merely” 400% improvement isn’t enough for the kind of effort that was put into this. I run the concurrent dictionary in a sampling profiler, with 20 threads, and I got the following results. </p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-How-does-this-thing-wor_9EA4/image_4.png"><img width="1137" height="246" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-How-does-this-thing-wor_9EA4/image_thumb_1.png" border="0"></a></p><p>On the other hand, using FASTER for the same scenario gives:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-How-does-this-thing-wor_9EA4/image_6.png"><img width="1117" height="71" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-How-does-this-thing-wor_9EA4/image_thumb_2.png" border="0"></a></p><p>This is really interesting. You can see that the FASTER option spends all its time in either: InternalUpsert or inside the RunYcsb method (which is actually the store.Read() method that was inlined). </p><p>What is more interesting is that there are no additional calls there. The InternalUpsert call is 219 lines of code, and the code uses [MethodImpl(MethodImplOptions.AggressiveInlining)] quite aggressively (pun intended). On the other hand, the ConcurrentDictionary implementation has to make virtual method calls on each call. </p><p>There are several ways to handle this, including using generic struct that can eliminate most of the virtual calls. This is effectively what FASTER is doing, without the generics. FASTER also benefits from pre-allocating everything upfront. If you’ll look at the profiler results, you can see that these are the major source of “slowness” in the benchmark.</p><p>Given the nature of the benchmark, I feel that it is unfair to compare FASTER to persistent data stores and it should be compared more closely to a concurrent hash map. Given that this is effectively what FASTER is doing in this showcase benchmark, that seems a lot more fair. I checked the literature and we have <a href="https://arxiv.org/pdf/1601.04017.pdf">this paper</a> talking about concurrent hash maps where we see (Figure 2.a) numbers that near to 300 millions ops/sec for pure writes and 600 millions ops/sec for reads. </p>https://www.ayende.com/blog/184289-A/reviewing-faster-lets-check-these-numbers?Key=fcf4b9d1-86b8-4daa-9414-8f4e55e458bbhttps://www.ayende.com/blog/184289-A/reviewing-faster-lets-check-these-numbers?Key=fcf4b9d1-86b8-4daa-9414-8f4e55e458bbWed, 29 Aug 2018 09:00:00 GMTReviewing FASTER: Let’s start with managed code<p><a href="https://ayende.com/blog/184225-A/reviewing-faster-reading-the-paper?key=ced524c829bc4a1fae86a3c7facc325b">Last post</a> commented on the FASTER paper, now I’m getting to reading the code. This is a pleasant change of scenery for me, since FASTER is written in C# (there is a C++ port that I’ll also be going through later). As someone who spend most of their time in C#, that make it much easier to go through the code. On the other hand, this was <em>clearly</em> written by someone who spent most of their time in C++. This means that naming conventions and the general approach to writing the code sometimes directly contradict C# conventions. Some of that really bugs me.</p><p>The first thing I did was to try to run the benchmark on my own machine, to get relative numbers. It died with an <a href="https://github.com/Microsoft/FASTER/issues/15">AccessViolationException</a>, which was a disappointment. I’m going to ignore that and just read through the code. One thing that I did noticed when reading through the benchmark code is that this piece:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Lets-start-with-the-cod_7CAD/image_2.png"><img width="416" height="224" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Lets-start-with-the-cod_7CAD/image_thumb.png" border="0"></a></p><p>This maps closely to some of the things they mentioned in the paper, where each thread refresh every 256 ops and complete pending operations every 65536 ops. The reason I call this out is that having this done in what effectively is a client code is a bad idea in term of design. The benchmark code is operating under continuous stream of operations and can afford to amortize such things. However, real code need to deal with client code that <em>isn’t </em>well behaving and you can’t assume that you’ll be operating in this kind of environment.</p><p>Based on the semantics discussed in the paper and what the code is showing me, I would assume that the general model for actually using this is to spawn off a bunch of threads and then listen to some data source. For example, a socket. On each network read, the thread would apply a batch of operations. Note that this means that you have to deal with threads that may be arbitrarily delayed. I would expect that under such a scenario, you’ll see a very different performance profile. You won’t have all threads working in tandem and staying close to one another. </p><p>When getting to the code itself, I started with the native portions, trying to figure out what FASTER is doing with the lowest level of the system. It is mostly advanced file operations (things like telling the OS to don’t defrag files, allow to allocate disk space without zeroing it first, etc). As far as I can see, this isn’t actually being called from the current code, but I assume that they at least tested out this approach. Another native code they have is related to calling __rdtsc(), which they use in the HiResTimer class. </p><p>This can be replaced completely by the .NET Stopwatch class and I believe that dropping the <em>adv-file-ops</em> and <em>readtsc </em>native projects is possible and straightforward, allowing for a fully managed FASTER impl. Note that it is still using a <em>lot</em> of interop calls, it seems, but at least so far, I think that most of them are not necessary. To be fair, given the way the code is structured, a lot of the native code is about I/O with pointers, which until the Great Spanification in .NET Core was PITA to deal with.</p><p>In general, by the way, the code reads more as a proof of concept than a production codebase. I notice this in particular with the general approach for errors handling. Here are two examples:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Lets-start-with-the-cod_7CAD/image_4.png"><img width="764" height="100" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Lets-start-with-the-cod_7CAD/image_thumb_1.png" border="0"></a></p><p>This is from the native code, from which it is a PITA to return errors. However, <em>writing to the console</em> <em>from a library </em>is not an error reporting strategy. What really bugged me was this code, from the MallocFixedPageSize code:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Lets-start-with-the-cod_7CAD/image_6.png"><img width="532" height="266" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Lets-start-with-the-cod_7CAD/image_thumb_2.png" border="0"></a></p><p>If there is any error in the process of pinning memory, just ignore it? Leave the memory pinned? </p><p>Here is another example that made me cringe:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Lets-start-with-the-cod_7CAD/image_8.png"><img width="528" height="192" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Lets-start-with-the-cod_7CAD/image_thumb_3.png" border="0"></a></p><p>Moving on to the <em>CodeGen</em> directory, it gets interesting. The paper talks about dynamic code generation, but I didn’t expect to see such wide usage of this. In particular, large sections of the code (13 files, to be exact, over 6000 lines of code) are dynamically loaded, transformed and compiled on the fly when you create the hashtable. </p><p>I understand the <em>reasoning </em>for this, you want to get the JIT to compile the best possible code that it can for the specific operations you execute. However, this make it pretty hard to follow what is going on there. In particular, code generating code make it harder to follow what end up actually going on. There are also better ways to do it. Either through generic struct parameters to specialize the code or only generating the dedicated connecting methods as needed and not recompiling large portions on the fly. </p><p>The <em>Device</em> directory isn’t really interesting. Mostly pretty trivial I/O operations, so I’m not going to discuss it in depth.</p><p>Next, getting to the Epoch, which was really interesting to read in the paper. The actual implementation raise a few questions. The Epoch value in an 32 bits integer, that means that it will wrap fairly quickly. It looks like the Epoch is bumped every time you need a new page. Given the operations rate that are reported, I would expect it to happen on a regular basis (this is also required for the system to progress properly and sync up). My reading is that wrapping of the Epoch counter will result in Bad Things Going On.</p><p>There there is this:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Lets-start-with-the-cod_7CAD/image_10.png"><img width="499" height="86" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Lets-start-with-the-cod_7CAD/image_thumb_4.png" border="0"></a></p><p>Size, in this case, is always set to 128. The size of Entry is 64 bytes, this means that this call will allocate 8,320 bytes in Gen0, immediately pin it and never let it go. This is going to result in memory fragmentation. It would be better to allocate this on the Large Object Heap and avoid the issue. In fact, the same issue can be seen in the memory allocation, where the code does:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Lets-start-with-the-cod_7CAD/image_12.png"><img width="521" height="62" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Lets-start-with-the-cod_7CAD/image_thumb_5.png" border="0"></a></p><p>In This case, PageSize is 65,536, however, and given any value except a byte, this will automatically go to the Large Object Heap anyway. I’m going to be a bit ungenerous here, but I’m not sure if this was intentional or not.</p><p>I’m firmly convinced that this is pure POC code, here is the snippet that finally did it for me, from the BumpCurrentEpoch() code. </p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Lets-start-with-the-cod_7CAD/image_14.png"><img width="618" height="203" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER-Lets-start-with-the-cod_7CAD/image_thumb_6.png" border="0"></a></p><p>Note that I don’t mind the Thread.Sleep() here, it make sense in context, but the Console.WriteLine cinched the deal for me. This is not something that you can take a use, but something to look at as you implement this properly.</p><p>I have to say that I find it very hard to go through the code, mostly because it’s in C# and there are certain expectations that I have from the code which are routinely violated. I think that I’m going to stop reading the C# codebase and switch over to the C++ implementation. I expect this to be more idiomatic, both because it is the second time this was written and because the people writing the code are clearly more using to writing in C++.</p>https://www.ayende.com/blog/184257-A/reviewing-faster-lets-start-with-managed-code?Key=478acffa-3cca-45da-af9d-e4c00a6d378dhttps://www.ayende.com/blog/184257-A/reviewing-faster-lets-start-with-managed-code?Key=478acffa-3cca-45da-af9d-e4c00a6d378dTue, 28 Aug 2018 09:00:00 GMTReviewing FASTER: Reading the paper<p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_D137/image_2.png"><img width="262" height="287" title="image" align="right" style="border: 0px currentcolor; border-image: none; float: right; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_D137/image_thumb.png" border="0"></a>The <a href="https://github.com/Microsoft/FASTER">FASTER</a> is a fast key-value store from Microsoft. It’s <a href="https://www.microsoft.com/en-us/research/uploads/prod/2018/03/faster-sigmod18.pdf">paper</a> was published a while ago and now that the source is available, I thought that I would take a peek. You might say that I have a professional interest in such a project <img class="wlEmoticon wlEmoticon-smile" style="" alt="Smile" src="https://ayende.com/blog/Images/Open-Live-Writer/Reviewing-FASTER_D137/wlEmoticon-smile_2.png">.</p><p>I decided to read the paper first before reading the code, because I want to figure out what <em>isn’t</em> in the paper. The implementation details beyond the abstract are what I find most interesting, to be honest. But if I’m going to read the paper, I’m also going to add some comments on it. Please note that the comments are probably not going to make much sense unless you read the paper.</p><p>The <em>Epoch Basic</em> section explains how they use a global epoch counter with a thread local value and a shared table that marks what epochs are visible to each thread. They use this to provide synchronization points, I assume (don’t know yet). This resonates very strongly with how LMDB’s global transaction table operates. </p><p>I like the concept of the drain list which is executed whenever an epoch become safe. I would assume that they use that to let the clients know that their operation was accepted and what was its state.</p><p>I wasn’t able to figure out what they use the <em>tag</em> for in the hash bucket entry. I think that the way it works is that you have K hash buckets and then use the first K bits to find the appropriate bucket, then scan for a match on the last 15 bits. I’m not really sure how that work with collisions, though. I assume that this will be clearer when I get to the code. I like the two phase scan approach to ensure that you have no conflicts when updating an entry. </p><p>The paper keeps repeating the speed numbers of 160 millions ops/sec and talking about 20 millions ops / sec as being “merely”. Just based on my reading so far, I don’t see how this can happen. What is interesting to me is what is the definition of ops. Is it something like incrementing the same (small) set of counters? If that is the case, than the perf numbers both make more sense and are of less interest. Typically when talking about ops / sec in such scenarios we talk about inserts / updates to individual documents / rows / objects. Again, I don’t know yet, but that is my thinking so far.</p><p>One thing that I find sad is that this isn’t a durable store. A failure in the middle of operations would cause some data to be completely lost. It seems like they have support for checkpoints, so you don’t lose everything. However, I’m not sure how often that happens and the benchmarks they are talking about were run without it. Interestingly enough, the benchmarks were run without garbage collection. I haven’t gotten to the discussion on that yet, so I’m not exactly what that means Another missing feature here is that there is no support for atomicity. You cannot ensure that two separate operations will run as a single atomic step. </p><p>The benchmark machine is 28 cores with 256GB RAM and 3.2 TB NVMe drive. This is a really nice machine, but from the get go I can tell that this is not going to be a fair comparison to&nbsp; most other storage engines. Faster is explicitly designed to work mostly in memory and with high degree of parallelism. This is great, but it gives us some important features (atomic batches and durability, also known as transactions). The data size they tested are:</p><ul><li>250 million records with 8 bytes keys &amp; 8 bytes values – Just under <strong>4GB </strong>in total.</li><li>250 million records with 8 bytes keys &amp; 100 bytes values – Just under <strong>32GB </strong>in total.</li></ul><p>I’m not sure why they think that this is going to provide larger than memory setup. In particular, they mention a memory budget of 2GB, but I assume that this is just internal configuration. There is also going to be quite a lot of memory cached in the OS’ page cache, for example, and I don’t see anything controlling that. Maybe I’ll when I’ll go through the code, though.</p><p>Okay, the garbage collection they refer to is related to how they compact the log. They use an interesting approach where they just discard it at a some point, and any pointer to the removed section is considered to be deleted automatically. That is likely to be very efficient, assuming that you don’t care about older data. </p><p>All in all, I feel that I have a vague understanding on how Faster works and a lot better grasp on what it does and how it is utilized. I’m looking forward to diving into the code.</p>https://www.ayende.com/blog/184225-A/reviewing-faster-reading-the-paper?Key=ced524c8-29bc-4a1f-ae86-a3c7facc325bhttps://www.ayende.com/blog/184225-A/reviewing-faster-reading-the-paper?Key=ced524c8-29bc-4a1f-ae86-a3c7facc325bMon, 27 Aug 2018 09:00:00 GMTReading the NSA’s codebase: LemonGraph review–Part VII–Summary<p>As the final post in this series, I decided to see how I can create a <em>complex</em> query. Given the NSA’s statement, I decided to see if I can use <a href="https://github.com/NationalSecurityAgency/lemongraph">LemonGraph</a> to find a dog of interest. In particular, given our graph, I wanted to find start with a particular dog and find another dog that likes this dog that also like a dog that dislike the original.</p><p>As a reminder, here is the graph:</p><p><img alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_8B1B/image_thumb_3.png"></p><p>And starting from Arava, I want to find a dog that likes Arava that also likes a dog that isn’t liked by Arava.</p><p>The LemonGraph query language isn’t very expressive, or at least I’m not familiar enough with it to make it work properly. I decided on the following query:</p><blockquote><p>n(type="dog", value="arava")-&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @e(type="likes", value="yes")-&gt;<br>n(type="dog")-&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @e(type="likes", value="yes")-&gt;<br>n(type="dog")-&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @e(type="likes", value="no")-&gt;<br>@N(type="dog", value="arava")</p></blockquote><p>This is a bit of a brute force method to do this. It encodes the path directly. There are a few minor things that might not be obvious here. The @ prefix means don’t return this to the user and the N() indicates that we shouldn’t filter already seen values. I can certainly see how this can be useful for certain things <img class="wlEmoticon wlEmoticon-smile" style="" alt="Smile" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_CB05/wlEmoticon-smile_2.png">. I wonder if LemonGraph has a better way to express such a query.</p><p>This is the first time I actually reviewed this kind of codebase, where some things are done in C and some in Python. It was quite interesting to see the interaction between them. The codebase itself is really interesting, but I found it really hard to follow at times. The love affair with tuples and dynamic behavior made the code non trivial and likely is going to cause maintenance issues down the line. It is also quite obvious that this is intended for internal consumption, with very little time or effort spent on “productization”. By that I meant things like better query errors and more obvious thing to do. </p><p>It has an interesting approach to solving the problem of graph queries and I’ve learned quite a few things from it. </p><p><a name="comments"></a>https://www.ayende.com/blog/184102-C/reading-the-nsas-codebase-lemongraph-review-part-vii-summary?Key=807f8f07-e2b5-4f29-bad6-747ad30514behttps://www.ayende.com/blog/184102-C/reading-the-nsas-codebase-lemongraph-review-part-vii-summary?Key=807f8f07-e2b5-4f29-bad6-747ad30514beMon, 13 Aug 2018 09:00:00 GMTReading the NSA’s codebase: LemonGraph review–Part VI–Executing queries<p>After going over many layers inside <a href="https://github.com/NationalSecurityAgency/lemongraph">LemonGraph</a>, I think we are ready to get to the real deal. We know how LemonGraph starts to execute a query. It find the clause that is likely to have the least number of items and starts from there. These are the seeds of the query, but how is it going to actually process that?</p><p>Here is my graph:</p><p><img alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_8B1B/image_thumb_3.png"></p><p>And here is the query that I want to run on it: n()-&gt;e(type="likes", value="yes")-&gt;n()</p><p>LemonGraph detects that the cheapest source of seeds for this query is the edge and provides these to the <em>MatchCTX </em>class which does the actual processing. Let’s explore how it is working on a single seed. </p><p>The actual behavior starts from the <em>matches()</em> method, which starts here:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_2.png"><img width="405" height="478" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_thumb.png" border="0"></a></p><p>As usual for this codebase, the use of tuples for controlling behavior is prevalent and annoying. In this case, the <em>idx</em> argument controls the position of the target in the query, whatever this is the start, end or somewhere in the middle. The deltas define what direction the matches should go and the stops where you must stop searching, I think.</p><p>Now that we setup the ground rules, the execution is as follows:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_4.png"><img width="1108" height="310" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_thumb_1.png" border="0"></a></p><p>In this case, because the seed of our query is the edge (which is in the middle) it determines that <em>deltas </em>are (1, –1) and <em>stops </em>are (2, 0). We’ll also go to the first clause in the if statement. The <em>link</em> variable there controls whatever we should follow links going in or out. </p><p>There is a <em>lot</em> of logic actually packed into the <em>link</em> initialization. The <em>self.link </em>is an array that has ‘next’ and ‘prev’ as its values, so the idea is that it find what property to look at, then use the dictionary syntax to get the relevant property and decide what kind of direction it should go. </p><p>We’ll focus on the <em>_recurse()</em> method for now. In this case, we are being passed:</p><ul><li>idx = 1</li><li>delta = 1</li><li>stop = 2</li></ul><p>Here is the actual method:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_6.png"><img width="714" height="376" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_thumb_2.png" border="0"></a></p><p>As you can see, we first validate that the match is valid (this is where we mostly check other properties of the value that we are currently checking, filtering them in place). Usually the <em>do_seen </em>will be set to <em>True</em>, which will ensure that we only traverse each node once. The main iteration logic is done in combination with the <em>_next()</em> method, shown below:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_8.png"><img width="798" height="284" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_thumb_3.png" border="0"></a></p><p> The first line there shows usage of delta, which control in what direction to move. I’m not quite sure why the <em>filter</em> is set in the if statements, since it is also being set immediately after. This looks like redundant code that snuck in somehow. </p><p>The bulk of the work is shelled out to <em>iterlinks</em>, so let’s check what is going on there… I know that we are running on an edge here, so we need to look at the <em>Edge.iterlinks()</em> method:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_10.png"><img width="903" height="407" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_thumb_4.png" border="0"></a></p><p>There isn’t really much to tell here, it checks the filters and return the incoming or outgoing edges, nothing more. On the other hand, the <em>Node.iterlinks() </em>implementation is a bit simpler:<a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_12.png"><img width="689" height="157" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_thumb_5.png" border="0"></a></p><p>We’ll explore exactly how the edges are loaded for a node in a bit, however, I do want to note right now that the <em>_next() </em>method isn’t passing the types of the edge, even though it looks to me that it has this information. In that case, that would give a performance boost because it could filter a lot of edges in busy graphs.</p><p>Actually, I already looked at how iterators working in <a href="https://ayende.com/blog/184098-C/reading-the-nsas-codebase-lemongraph-review-part-iii-figuring-out-queries?key=3841c2073e084d37bf8d9df46662bb63">a previous post</a>, so I won’t go over all of that again. This is basically calling this method</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_14.png"><img width="1246" height="348" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_thumb_6.png" border="0"></a></p><p>The <em>graph_node_edges_in()</em> and <em>graph_node_edges_out()</em> methods are pretty simple, basically just scanning the <em>DB_TGTNODE_IDX</em> or <em>DB_SRCNODE_IDX </em>indexes, respectively. The <em>graph_node_edges()</em> however, is really strange. </p><p>Here it is:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_16.png"><img width="924" height="206" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_thumb_7.png" border="0"></a></p><p>It took me a few reads to figure out that this is probably a case of someone unpacking a statement for debugging but forgetting to remove the packed statement. The second return statement is ignored and likely stripped out as unreachable, but it <em>is </em>pretty confusing to read.</p><p>The <em>graph_iter_concat()</em> answers a question I had about how <em>graph_iter_t</em> is used, here is how it looks like:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_18.png"><img width="669" height="454" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_thumb_8.png" border="0"></a></p><p>This is using C’s support for passing an unknown number of parameters to a method. This basically builds a simple linked list, which also answers the usage of <em>_blarf() </em>function and its behavior a few posts ago.</p><p>So we are back were we started, we understand how the data flows into the Python code, now let’s go back and look at <em>_recurse()</em> and <em>_next()</em> calls.</p><p>Now I know a lot more about the behavior of the code, so this make sense. The <em>stop</em> argument to the <em>_recurse()</em> control the depth of the number of links that would be traversed in a particular query. Now that I know how this particular clause work, I understand how LemonGraph goes from the edge to the node in e()-&gt;n(), but I don’t know how it goes to back to find the full path. </p><p>The key for that are the calls to <em>push()</em>&nbsp; and <em>pop() </em>in the the <em>MatchCtx._recurse()</em> methods. These update the chain, like so:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_20.png"><img width="924" height="129" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_thumb_9.png" border="0"></a></p><p>In processing the query, we first append the edge to the chain:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_22.png"><img width="499" height="50" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_thumb_10.png" border="0"></a></p><p>The next step goes from the edge to it’s destination, giving us Oscar:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_24.png"><img width="471" height="70" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_thumb_11.png" border="0"></a></p><p>Remember that in the <em>matches()</em>, we got into this if clause:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_26.png"><img width="1095" height="204" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_thumb_12.png" border="0"></a></p><p>This is when we start scanning an edge, which first add things to the right, then it scan things to the <em>left </em>in the <em>_next()</em> method. Look at the <em>push() </em>method there. By the time we get to the <em>result()</em>, we have iterated both sides of the connection, giving us:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_28.png"><img width="490" height="91" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_A4B1/image_thumb_13.png" border="0"></a></p><p>That is a very clever way of doing things. </p><p>Let’s try doing things a little bit differently. I’m going to check a slightly different query: n(type=”dog”, value=”arava”)-&gt;e(type="likes", value="yes")-&gt;n()</p><p>If I understand things correctly, this is going to select the node as the starting point, because that is a lot more efficient. That will allow me to figure out the other side of this operation. I just run this through the debugger, and this is indeed how it actually works. </p><p>The usage of <em>yield</em> to pass control midway is not trivial to follow, but it ends up being quite a nice implementation. This is enough for this post. In my next one, I want to explore a few of the possible operations that exposed by LemonGraph.</p>https://www.ayende.com/blog/184101-C/reading-the-nsas-codebase-lemongraph-review-part-vi-executing-queries?Key=fc7bc515-816f-4026-a643-4c2ecde64b56https://www.ayende.com/blog/184101-C/reading-the-nsas-codebase-lemongraph-review-part-vi-executing-queries?Key=fc7bc515-816f-4026-a643-4c2ecde64b56Fri, 10 Aug 2018 09:00:00 GMTReading the NSA’s codebase: LemonGraph review–Part V–Query parsing<p>I <a href="https://ayende.com/blog/184098-C/reading-the-nsas-codebase-lemongraph-review-part-iii-figuring-out-queries?key=3841c2073e084d37bf8d9df46662bb63">said before</a> that I don’t want to get into the details of how <a href="https://github.com/NationalSecurityAgency/lemongraph">LemonGraph</a> is dealing with parsing the queries. Unfortunately, I can’t avoid that. There seems to be a lot of logic, magic and mystery in the <em>MatchLGQL()</em> class, which is critical to understanding how queries work.</p><p>The problem is that either my Python-fu is lacking or it is just really hard to figure out a non trivial codebase behavior in a dynamic language like python. I find it hard to figure out what data is stored where and how it is manipulated. Therefor, I decided to break with my usual custom and actually run the code in the debugger to try to follow what is going on there. I tried to run this on WSL, but it crashed horribly, so I had to spin up a VM and setup PyCharm on it. First time that I’m actually using that and the experience is pretty nice so far. Being able to inspect things directly means that it is much easier to figure out the behavior of the code. </p><p>In order to explore how queries work in LemonGraph, I created the following graph, which represents the relationships between my dogs:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_8B1B/image_8.png"><img width="590" height="624" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_8B1B/image_thumb_3.png" border="0"></a></p><p>Here is how this looks like in code:</p><script src="https://gist.github.com/ayende/ea9c670550691decc79104a8cac59d95.js"></script><p>This tells us to find all the dogs that like each other. And it finds:</p><ul><li>Arava –&gt; Oscar</li><li>Oscar –&gt; Arava</li><li>Oscar –&gt; Pheobe</li></ul><p>Now that we have a query that we can sink our teeth into, let’s figure out how this work, shall we? Inside the dreaded <em>MatchLGQL()</em> class, there are <em>all sorts</em> of regular expressions running on the parse this thing, but eventually we get to the partially processed parsed query:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_8B1B/image_10.png"><img width="939" height="90" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_8B1B/image_thumb_4.png" border="0"></a></p><p>This screen shot might explain why I wasn’t happy with the code structure for figuring out what is going on without debugging. The number of tuples here is quite amazing, and they are used everywhere. This make static analysis (as in, just reading the code) too hard for me. But with the debugger, that is much easier. If you are familiar with ASTs, this should be pretty easy to figure out.</p><p>Here is a piece of code that we already looked at (and criticized), this is in <em>munge_obj()</em> method, where it is deciding how to optimize the query:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_8B1B/image_14.png"><img width="647" height="355" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_8B1B/image_thumb_6.png" border="0"></a></p><p>This piece of code is critical for the performance of the system. And it is really hard to understand. Here is what is going on.</p><p>The <em>accel</em> array tell a later piece of code how to accelerate the query, using the type or type and value to start from a particular source. The <em>info</em> is used to carry state about particular clause in the query. Before this code run there is some code that builds the dictionary <em>d</em> which is used to figure out the filters on the particular clause. This is <em>fun</em>, because it is using missing a key lookup in the dictionary for control flow.</p><p>Let’s follow the logic?</p><ul><li>Line 2 - If the clause operates on a node, rank it as 6. If it is an edge, rank it as 7.</li><li>Line 6 – If the clause has a <em>type</em> specified, rank is as 4 if it is a node, 5 if it is an edge. Otherwise, abort the optimization.</li><ul><li>You might not see the “abort this optimization” in line 6, because it relies on the dictionary to throw if the key isn’t found. This is a common pattern in this code and something that I personally greatly dislike.</li></ul><li>Line 8 – it uses the <em>length </em>of the type as a metric for secondary ranking. I’m not quite sure why this is the case. I guess the code needed a tie breaker, but I can’t imagine why the length of a type would have any impact on performance.</li><ul><li>Unless, of course, the code assumes that shorter types are more common, and therefor will prefer to use the rare longer types?</li></ul><li>Line 10 – If there is a type <em>and</em> a value defined, that is even better. Note that again the is the ranking of node (2) and edge (3) which I find non obvious.</li></ul><p>Here are the results of the matches after they have been munged, I marked the ranking:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_8B1B/image_16.png"><img width="1372" height="94" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_8B1B/image_thumb_7.png" border="0"></a></p><p>Looking at this, this seems very strange, the <em>rank2</em> value is 1 in the second element, but I expected it to be the length of the string. As it turns out, this is not working directly on the string, it is working on the tuple of <em>possible values</em>, so the secondary ranking here is not based on the length of the type or the value but on the <em>number</em> of possible types and values that were specified for each clause.</p><p>The code judges that the best place to start this query is with the second entry, since it is the most specific option. This in turn takes us the the <em>seeds()</em> method that <a href="https://ayende.com/blog/184098-C/reading-the-nsas-codebase-lemongraph-review-part-iii-figuring-out-queries?key=3841c2073e084d37bf8d9df46662bb63">we previously covered</a>. In this case, the code is going to hit this branch:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_8B1B/image_18.png"><img width="679" height="154" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_8B1B/image_thumb_8.png" border="0"></a></p><p>This means that it is going to be iterating over all the edges of a particular type and filtering them in Python code. This is strange, because the on disk indexes actually support doing a direct query on the (type, value) directly and would probably be <em>much</em> cheaper in the case you have many values for a particular type of an edge. </p><p>In fact, just that is implemented for querying nodes by (type, value):</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_8B1B/image_20.png"><img width="837" height="130" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_8B1B/image_thumb_9.png" border="0"></a></p><p>I’m guessing that they are either don’t have a lot of queries on (type, value) on edges or not a lot of different values for edge types that they can optimize in this manner.</p><p>That is enough for now, I have a pretty good grasp of how queries are parsed and how they fetch data from the underlying storage. The next post will talk about how LemonGraph takes the seeds of the query and execute the actual graph operations on them. The code that does this is <em>tight </em>and will require a full post to explore properly.</p>https://www.ayende.com/blog/184100-C/reading-the-nsas-codebase-lemongraph-review-part-v-query-parsing?Key=54a3d1e0-b3a1-4313-90a2-a8b973e6e335https://www.ayende.com/blog/184100-C/reading-the-nsas-codebase-lemongraph-review-part-v-query-parsing?Key=54a3d1e0-b3a1-4313-90a2-a8b973e6e335Thu, 09 Aug 2018 09:00:00 GMTReading the NSA’s codebase: LemonGraph review–Part IV–Compressed, sortable integers<p>Before going over the actual query implementation, I wanted to talk about something that I just realized. I <a href="https://ayende.com/blog/184066-C/reading-the-nsas-codebase-lemongraph-review-part-i?key=d248365d55574326bec98ba8d9e6699c">said previously</a> that I don’t understand why <a href="https://github.com/NationalSecurityAgency/lemongraph">LemonGraph</a> is using its integer encoding method, because it is less efficient than using variant sized integer. What I didn’t take into account is that the method LemonGraph is using gives short, but sortable, integers.</p><p>Here is the encoding method:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_9B6/image_2.png"><img width="1099" height="211" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_9B6/image_thumb.png" border="0"></a></p><p>Now, let’s see what kind of binary output this will generate when given a few numbers:</p><p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_9B6/image_4.png"><img width="492" height="147" title="image" style="margin: 0px; border: 0px currentcolor; border-image: none; display: inline; background-image: none;" alt="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_9B6/image_thumb_1.png" border="0"></a></p><p>The key here is that the number of bytes is stored as the first item. This means that when we compare two numbers using <em>memcmp</em>(), the number with more bytes is considered greater. This is indeed the case, because if you need more bytes to store a number, it is obviously larger.</p><p>What about two numbers that have the same number of bytes? This is handled by simple binary comparison of the values. If they are the same size, the fact that the <em>encode()</em> output them in big endian format means that we can compare them using <em>memcmp()</em> and be done with it.</p><p>This is a really nice way to both keep the values sorted and to avoid storing the full 8 bytes for numbers that are usually much smaller.</p>https://www.ayende.com/blog/184099-C/reading-the-nsas-codebase-lemongraph-review-part-iv-compressed-sortable-integers?Key=75aba3eb-e62c-4bd1-8603-13bc434eefb8https://www.ayende.com/blog/184099-C/reading-the-nsas-codebase-lemongraph-review-part-iv-compressed-sortable-integers?Key=75aba3eb-e62c-4bd1-8603-13bc434eefb8Wed, 08 Aug 2018 09:00:00 GMTReading the NSA’s codebase: LemonGraph review–Part III - Figuring out queries<p>After <a href="https://ayende.com/blog/184097-C/reading-the-nsas-codebase-lemongraph-review-part-ii?key=a59721f61eb1452ba72c97627cbea623">figuring out</a> how <a href="https://ayende.com/blog/184066-C/reading-the-nsas-codebase-lemongraph-review-part-i?key=d248365d55574326bec98ba8d9e6699c">LemonGraph</a> is storing data on disk, my next task is to figure out how queries are handled. Here are some queries:</p> <p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_2.png"><img style="margin: 0px; border: 0px currentcolor; display: inline; background-image: none;" title="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_thumb.png" alt="image" width="480" height="290" border="0" /></a></p> <p>A query starts in the Python&rsquo;s Query class, where it is parsed by the <a href="https://github.com/NationalSecurityAgency/lemongraph/blob/master/LemonGraph/MatchLGQL.py">MatchLGQL</a> class. I scanned this code briefly, but this is basically doing query parsing into the in memory representation. This is typically ugly piece of code, and that holds true for this code as well. Python&rsquo;s dynamic nature also means that there isn&rsquo;t an easy to follow set of AST classes. I&rsquo;ll skip the details of query parsing and just see how it is actually handling the queries, then.</p> <p>I started to look into the query execution and got lost in details that I didn&rsquo;t understand. In particular, there is a very strong tie between the query parsing and the execution. More so than I expected. What brought this home was this piece of code, which is used to rank the most efficient manner in which you should start executing the query.</p> <p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_4.png"><img style="margin: 0px; border: 0px currentcolor; display: inline; background-image: none;" title="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_thumb_1.png" alt="image" width="907" height="603" border="0" /></a></p> <p>At this point, I think that the idea here is that when you start running a query, you want to start from the smallest set of seed nodes. the ranking here seems to be a nice way to go about doing just that, but I&rsquo;m not really sure yet how this is applied.</p> <p>This is later used to figure out what the best starting location is in the <em>Query.finalize() </em>method.</p> <p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_6.png"><img style="margin: 0px; border: 0px currentcolor; display: inline; background-image: none;" title="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_thumb_2.png" alt="image" width="836" height="150" border="0" /></a></p> <p>This all come together for me inside the <em>_adhoc()</em> method on Query, where the query is actually being run:</p> <p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_8.png"><img style="margin: 0px; border: 0px currentcolor; display: inline; background-image: none;" title="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_thumb_3.png" alt="image" width="738" height="173" border="0" /></a></p> <p>The <em>self.compiled</em> is the set of already parsed matches. On each of them, we create a context (which will track already visited nodes / edges) and start by finding the seeds on the query. Seeds are handled using&hellip; an interesting approach:</p> <p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_10.png"><img style="margin: 0px; border: 0px currentcolor; display: inline; background-image: none;" title="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_thumb_4.png" alt="image" width="843" height="412" border="0" /></a></p> <p>It took me a few reads to get what this code is trying to do and I still thing that this is an obnoxious way to write things. This basically does the other side of the ranking. It is using the ranking to decide which method to call. I think that an enum would be about three time more readable. Especially since a bit lower you have:</p> <p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_12.png"><img style="margin: 0px; border: 0px currentcolor; display: inline; background-image: none;" title="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_thumb_5.png" alt="image" width="711" height="176" border="0" /></a></p> <p>I have to say that the modulus usage is the sign of true genius. Or madness. I&rsquo;m not sure which, because I can&rsquo;t figure out what the history of this code is. This was the same way from the initial commit, but I think that this code has history from before the public release. And it might have a reason for this kind of code. But I don&rsquo;t like it and I think it would have been<em> much</em> easier to read if it wasn&rsquo;t using magic numbers all over the place.</p> <p>At any rate, let&rsquo;s assume that we have the simplest query, for all the nodes. This would send us to <em>txn.nodes()</em> method. This would be rank 6, by the way. Here is how this looks like:</p> <p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_14.png"><img style="margin: 0px; border: 0px currentcolor; display: inline; background-image: none;" title="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_thumb_6.png" alt="image" width="922" height="387" border="0" /></a></p> <p>As you can see, we have two modes here. If the rank was 6, we aren&rsquo;t sent a type. But if the rank was 4, we are getting a type. I&rsquo;m going to start from the search for types, which seems more interesting.</p> <p>Here is where we end up in the C code:</p> <p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_18.png"><img style="border: 0px currentcolor; display: inline; background-image: none;" title="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_thumb_8.png" alt="image" width="1344" height="464" border="0" /></a></p> <p>Yes, the <em>graph_nodes_type()</em> method is calling <em>_graph_nodes_edges_type()</em> method. That was confusing as well to me. The key here is the DB_NODE_IDX index there, which tell it to use a different tree for the lookups.</p> <p>The <em>graph_string_lookup()</em> is something that we already run into, this is the <em>__blob_resolve()</em> method, which is searching for the string id for the given type. The code starts to get interesting when we see the <em>graph_iter_new() </em>call:</p> <p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_20.png"><img style="margin: 0px; border: 0px currentcolor; display: inline; background-image: none;" title="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_thumb_9.png" alt="image" width="1081" height="408" border="0" /></a></p> <p>So we specify an iterator on the given prefix. From the previous post, you might recall that DB_NODE_IDX is specific as (type, val &ndash;&gt; id). So this does a search on the first item that matches the prefix.&nbsp; The <em>_cleanse_beforeID()</em> method will ensure that the <em>beforeID </em>is only valid if it represent a value that is between 1 and the max log id that was generated on the graph.</p> <p>The iterator we got from the <em>nodes()</em> method just implement&rsquo;s Python iteration interface, starting from the <em>graph_iter_new()</em> item, then calling <em>graph_iter_next()</em> until the end. This is implemented like so:</p> <p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_24.png"><img style="margin: 0px; border: 0px currentcolor; display: inline; background-image: none;" title="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_thumb_11.png" alt="image" width="819" height="295" border="0" /></a></p> <p>Here we see for the first time the actual usage of <em>beforeID</em>. I&rsquo;m not sure what this does yet, so we&rsquo;ll skip this for now and look at the <em>_iter_idx_nextID()</em> method and come back to it later. This method is quite interesting. We have the head of the iterator, which is set to true in the iterator init. I&rsquo;m not sure what this means yet. What seems to be interesting is that <em>_blarf()</em> method, which I understand to be a cry of frustration (I had to look it up).</p> <p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_26.png"><img style="margin: 0px; border: 0px currentcolor; display: inline; background-image: none;" title="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_thumb_12.png" alt="image" width="725" height="647" border="0" /></a></p> <p>I&rsquo;m not sure what the <em>next</em> pointer is doing there at all. We&rsquo;ll look at that after I&rsquo;ll inspect (with some measure of dread) the <em>_blarf()</em> function.</p> <p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_28.png"><img style="margin: 0px; border: 0px currentcolor; display: inline; background-image: none;" title="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_thumb_13.png" alt="image" width="996" height="294" border="0" /></a></p> <p>To start with, I love the use of <em>goto</em> instead of <em>return</em> statements. I understand that this may be a coding convention here and this is used to clearly mark when resources are supposed to be disposed, but still&hellip;</p> <p>The <em>iter_next_key()</em> ends up moving the cursor (and validating that the prefix is still valid). The <em>_parse_idx_logID()</em> call is here:</p> <p><a href="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_30.png"><img style="margin: 0px; border: 0px currentcolor; display: inline; background-image: none;" title="image" src="https://ayende.com/blog/Images/Open-Live-Writer/Reading-the-NSAs-codebase-LemonGraph-rev_14AE1/image_thumb_14.png" alt="image" width="756" height="286" border="0" /></a></p> <p>And this is just a fancy way of saying, gimme the last value in this buffer.</p> <p>To understand what is going on, let&rsquo;s go back a bit and understand what is going on here. We are actually scanning the index <em>DB_NODE_IDX</em>. And that index has the value of (type, val, id). Since the iteration is done in sorted order, this means that you can iterate over all the matches that match the type you want. The use of <em>beforeID</em> for filtering here, however, is interesting. I wonder how common is the use of historical queries like this are. Because if you need to skip over a lot of items, this will result in an O(N) operation while you are scanning and discarding a lot of data.</p> <p>Anyway, there doesn&rsquo;t seem to be any use of the <em>graph_iter_t.next</em> in this code path, so I&rsquo;ll leave it for now. The iteration over all the nodes is done with the exact same method, but without specifying any prefix, which means that it matches everything.</p> <p>I have to admit that at this point, I don&rsquo;t really get how it process the more complex queries. I had to give up the &ldquo;let&rsquo;s not run the code&rdquo; and try a few things out. Surprisingly, on WSL, the code just cause segmentation fault. I tracked it down to something in cffi, but I didn&rsquo;t care that much. I created a simple ubuntu machine and played with it for a bit. So far, we just looked at how we get the first seeds of the query. A lot of the smarts seems to be hidden in the <em>MatchCTX</em> class.&nbsp; In particular, the <em>matches()</em> method seems to be doing a lot of magic.</p> <p>I&rsquo;m going to concentrate on that in my next post.</p>https://www.ayende.com/blog/184098-C/reading-the-nsas-codebase-lemongraph-review-part-iii-figuring-out-queries?Key=3841c207-3e08-4d37-bf8d-9df46662bb63https://www.ayende.com/blog/184098-C/reading-the-nsas-codebase-lemongraph-review-part-iii-figuring-out-queries?Key=3841c207-3e08-4d37-bf8d-9df46662bb63Tue, 07 Aug 2018 09:00:00 GMT