<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Ori Cohen on Medium]]></title>
        <description><![CDATA[Stories by Ori Cohen on Medium]]></description>
        <link>https://medium.com/@ori-cohen?source=rss-1b31f7086ae3------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*H5KTOoBeRiWd1TL_nzzH0Q.png</url>
            <title>Stories by Ori Cohen on Medium</title>
            <link>https://medium.com/@ori-cohen?source=rss-1b31f7086ae3------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 03:41:40 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@ori-cohen/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[The ART of efficient lookup]]></title>
            <link>https://ori-cohen.medium.com/the-art-of-efficient-lookup-6bc5ebc3f5e3?source=rss-1b31f7086ae3------2</link>
            <guid isPermaLink="false">https://medium.com/p/6bc5ebc3f5e3</guid>
            <category><![CDATA[surrealdb]]></category>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[data-structures]]></category>
            <category><![CDATA[database]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Ori Cohen]]></dc:creator>
            <pubDate>Wed, 23 Oct 2024 09:35:37 GMT</pubDate>
            <atom:updated>2024-10-23T09:35:37.906Z</atom:updated>
            <content:encoded><![CDATA[<h3>The ART of efficient lookup in SurrealKV</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*X-1J7QLkk3MR4P2B237mfQ.jpeg" /><figcaption>Image generated by author using Gemini</figcaption></figure><p>SurrealKV, the new key-value storage built by the SurrealDB team, is backed by a powerful data structure that enables it to have several important features:</p><ul><li>Performance</li><li>Low memory usage</li><li>Versioning</li><li>Multiple concurrent readers and writers</li></ul><p>This data structure is the Versioned Adaptive Radix Trie, or VART. In this post I’ll explain this data structure by building up to it from a simple concept to the powerhouse that VART is. Afterwards, we’ll look at how this structure is uniquely suited to the way SurrealDB works.</p><p>Let’s dive in!</p><h3>ART — Intuition</h3><p>The ART data structure, or Adaptive Radix Tree, was first proposed in a 2013 research paper as a potential solution to enhance the efficiency of key-value lookup operations. Traditional key-value data structures, such as hash tables and binary search trees, often face performance bottlenecks when dealing with large datasets or dynamic workloads. ART was designed to address these limitations by leveraging a more adaptive and efficient approach to key storage and retrieval.</p><h4>The OG Prefix Tree (Trie)</h4><p>As the name suggests, this data structure is an evolution of a classical prefix tree (a.k.a trie).</p><p>A trie is a search tree that is used to store specific keys from a set. A trie is built up such that each node in the tree is a letter, and so words sharing a common prefix will share a root path. The advantage of this structure is that it is relatively efficient to look up a key, a search being only <em>O(m)</em>, where <em>m</em> is the length of the key in question.</p><p>Let’s look at an example:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/246/1*f4VW7QlKCSyLHdpibNjVPQ.png" /></figure><p>Each edge node (or <em>leaf</em> as it’s usually called), will contain a value associated with the key up to that leaf.</p><p>So far so good, right?</p><h4>Radix Trie</h4><p>One thing that’s clear with the above example is that we can improve it with one some simple modification — combining nodes with only one child with that child. This way we don’t have to traverse multiple nodes where only one hop is needed. This trick hugely reduces both the memory footprint and the lookup time in the suffix tree.</p><p>A lookup tree with nodes combined is such a fashion is called a <em>Radix Trie. </em>Applying it to our example:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/551/1*QD-OZmMaOHIg1U6i6SDVnA.png" /></figure><p>Much simpler, isn’t it? But can we do better?</p><h4>Let’s Adapt</h4><p>One further optimization that we can apply is by using nodes of different sizes.</p><p>Think about it — a node has to have an array of pointers that connect it to the child nodes. Even if we only have one letter in each node, then the maximal amount of pointers we’ll need is not small (the number of letters+numbers+symbols), and the more of those pointers that are empty, the more wasted space we have.</p><p>Knowing this, the logical leap is to use several sizes nodes (specifically 4, 16, 48, 256), each containing a different number of pointers. This way the percentage of unused pointers is kept relatively low, and both memory usage and lookup time improves.</p><p>Our example doesn’t change much, but the smaller boxes indicate the fact that we are now using smaller nodes, using up less space.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/461/1*UKp4KxO84n5UyJZWbl78qQ.png" /></figure><h3>But how good is it, really?</h3><p>This <a href="https://db.in.tum.de/~leis/papers/ART.pdf">original paper</a> on ART reports some impressive numbers.</p><h4>Memory usage</h4><p>Compared to various other tree structures of differing sizes (the <em>s</em> parameter is the number of pointers in a normal radix tree node), ART provides an exponential advantage, as can be seen:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/522/1*cBe_5cljPqGNxm1QGzWBIw.png" /><figcaption>ART memory size, source: ART paper</figcaption></figure><p>This means that ART is not saving a little bit of space, <strong>it’s 1000 times better </strong>than the biggest trees!</p><h4>Runtime</h4><p>In addition to having a smaller memory footprint, the lookup time also rivals optimized lookup trees (FAST) or hash tables (HT), all while having a much less memory.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/529/1*CZNu8Tps_fcAkoOof26fHQ.png" /><figcaption>Lookup time VS other solutions, source: ART paper</figcaption></figure><p>A key point here is the dense and sparse modes. ART is more efficient the “closer” the keys are together and therefore the less nodes need to be created. In this benchmark, the authors inserted a range of 16 million keys for the dense example, and chose 16M random keys for the sparse one.</p><p>As we’ll immediately see, this is one reason this structure suites Surreal so well.</p><h3>How SurrealKV uses Versioned ART under the hood</h3><p>In a <a href="https://medium.com/@ori-cohen/surrealkv-diving-deep-with-the-new-storage-engine-in-surrealdb-2-0-5c8d276aaaf6">previous post</a>, I looked at SurrealKV, the new key-value storage engine that the Surreal team announced. This engine uses Versioned ART, or VART, as the underlying storage engine.</p><h4>Why is this the appropriate data structure?</h4><p>In this section I aim to convince you that not only is this the appropriate data structure for SurrealDB’s use-case, it is uniquely suited for it.</p><p>In order to do that I’d like to review how SurrealDB stores its data as key-value pairs. Each key is constructed in hierarchical fashion, a record for example will be saved with a key of the format:</p><pre>* namespace_name<br>* database_name<br>* table_name<br>* object_key</pre><p>Now let’s imagine how the ART will look. Because of the use of shared prefixes we know that all the records will be children of the same node, something like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/521/1*nFQ_lCqoJRqatMicoS3W1A.png" /></figure><p>This in turn means that accessing multiple pieces of data will require less reads, as the relevant node is already accessed!</p><p>More than that, since Surreal’s keys are dense (in the sense that they are usually grouped under similar nodes), this takes advantage of ART’s performance benefits as well as it’s reduced memory usage.</p><h4>Why version?</h4><p>As I’ve discussed in the SurrealKV post, querying past versions of the data is essential for multiple use cases when using a database, such as preventing data loss, undoing changes, tracking updates and so on.</p><p>Surreal enables this by introducting a new VERSION keyword that can be added to queries:</p><pre>-- Example from Surreal&#39;s blog post<br>SELECT * FROM user VERSION d&#39;2024-08-12T11:03:00Z&#39;<br>[[{ id: user:john, name: &#39;John v1&#39; }]]</pre><p>Behind the scenes this is accomplished using a <em>versioned ART</em> that allows for querying the same key across different versions. They’ve expanded upon the subject in <a href="https://surrealdb.com/blog/vart-a-persistent-data-structure-for-snapshot-isolation">their post</a>.</p><h4>How do they do it?</h4><p>Now that we understand the data structure behind the storage engine, expanding it for multiple versions is almost trivial.</p><p>Instead of each leaf containing a single value, it will now contain a list of values and the dates they were inserted. In such a manner, accessing the latest version is the default, and the ability to filter for certain insertion times becomes trivial.</p><p>Under the hood, there are several more aspects that make this technique efficient.</p><ol><li>VART utilizes a Copy-on-Write (CoW) technique, which means that it can hold several copies of the entire tree in-memory, with shared pointers to unchanged data (utilizing Rust’s Arc struct, which is an Atomic Reference Counted Pointer, only changed data creates a new element, with all previous pointers using the same underlying data).</li><li>By creating <em>snapshots</em> of the data while a transaction is running, we can ensure that each transaction works on a seperate copy of the data and does not interfere with other transactions. The transaction that finishes first will successfully commit it’s version to the main tree and transactions that will try to change the same nodes as the finished transaction will see a different version when they try to commit and fail.</li><li>On the other hand, two transactions that don’t change the same nodes can run and complete concurrently without interfering with each other, making this data structure able to handle multiple transactions at the same time.</li></ol><h3>Conclusion</h3><p>In this post we’ve looked in-depth at the VART data structure and how it powers SurrealKV, SurrealDB’s new storage engine. We’ve seen how it improves upon classical search trees and how it is uniquely suited for SurrealDB’s needs.</p><p>We now also understand how this allows SurrealKV the ability to “time-travel” by saving multiple version of the values and accessing the correct one.</p><p>I hoped you enjoyed this post, see you next time!</p><p>Interested in learning more about how SurrealDB works? All my posts are available <a href="https://ori-cohen.medium.com/list/surrealdb-deep-dive-ddefbefaf1ef">here</a>.</p><p>Have specific questions or requests? Let me know in the comments.</p><p>Please clap and follow for more, I’ll be continuing my deep dives into SurrealKV and SurrealDB.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6bc5ebc3f5e3" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How continuously evolving my Obsidian vault exponentially improved my experience]]></title>
            <link>https://ori-cohen.medium.com/how-continuously-evolving-my-obsidian-vault-exponentially-improved-my-experience-17b209ad570e?source=rss-1b31f7086ae3------2</link>
            <guid isPermaLink="false">https://medium.com/p/17b209ad570e</guid>
            <category><![CDATA[personal-knowledge-system]]></category>
            <category><![CDATA[notetaking]]></category>
            <category><![CDATA[productivity]]></category>
            <category><![CDATA[obsidian]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Ori Cohen]]></dc:creator>
            <pubDate>Thu, 17 Oct 2024 20:58:17 GMT</pubDate>
            <atom:updated>2024-10-17T20:58:17.178Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JGKT5UpAVqSqTGuoxGBWMg.jpeg" /><figcaption>Image generated by author using Gemini</figcaption></figure><p><em>Disclosure as per </em><a href="https://blog.medium.com/medium-is-for-human-storytelling-not-ai-generated-writing-b5f7ffdc96cf"><em>Medium’s AI policy</em></a><em>: in this post I’ve used Microsoft Copilot for editing.</em></p><p>For the past year, I’ve been using Obsidian, the personal knowledge management app, for almost everything I’ve been doing, from task management, through meeting notes and up to planning my Medium posts. In this post, I’d like to share with you the one key insight I understood about working with this wonderful tool.</p><p>First, What is Obsidian? Obsidian isn’t just another note-taking app; it’s a digital garden for your ideas, thoughts, and knowledge. Think of it as a personal knowledge base that interlinks your notes, creating a web of information that’s tailored to your mind. Imagine a place where your thoughts can evolve, connect, and grow organically, transforming isolated notes into a cohesive network of ideas. Whether you’re a student, a writer, or just someone who loves to keep your thoughts organized, Obsidian offers a unique approach to managing your knowledge.</p><p>In this post I’d like to address a key point that in my opinion isn’t discussed enough — You <em>need </em>to personalize your vault. More than that — You should do it every single day. I’ll show this concept through several personal examples.</p><h3>Why?</h3><p>Personalizing your vault is the secret sauce to making Obsidian truly work for you. Your vault should reflect how your mind processes information and how you best work. Customizing the layout, choosing the right plugins, and organizing your notes in a way that feels intuitive can significantly enhance your productivity and creativity.</p><p>Personalizing your vault transforms Obsidian from a generic note-taking app into a powerful personal knowledge management system that’s uniquely yours, one that you feel comfortable working with every day. Simply copying templates from blogs or YouTube channels might be a great source of inspiration, but if you don’t modify them to match you how you feel is best, you will feel like there is a high cost to using all of these wonderful tools.</p><p>Now, let’s delve into how you can start customizing and get the most out of your digital garden.</p><h3>How?</h3><p>Before we begin discussing several aspects of personalizing your vault, you must understand that this is a dynamic and open-ended process. The vault I’m using today is the 200th iteration of the vault I started with one year ago. Every day you should look at how you work, what suits you and what doesn’t, and make small changes to make your work slightly easier, smooth over a pain point, and automate a small corner. Those small changes build up over time and bloom into a beautiful design that fits you personally.</p><h4>Lines of thought</h4><p>Here are a few general aspects that make up your vault. In each you have many choices that can shape and change the way you work, and I’ll show a few examples further on in the post.</p><p>Start by identifying what kind of structure suits you best. Are you a visual thinker? Utilize the Excalidraw to draw out your notes. Prefer a more linear approach? Keep your folders and tags well-organized. The beauty of Obsidian lies in its flexibility — it can adapt to whatever system you prefer.</p><p>Although Obsidian was originally meant to be a text-based editor, there are now many ways to lay out your thoughts more visually and dynamically. The built-in solution by Obsidian is <a href="https://obsidian.md/canvas">Obsidian Canvas</a>, but the community-driven <a href="https://forum.obsidian.md/t/excalidraw-full-featured-sketching-plugin-in-obsidian/17367">Excalidraw plugin </a>is a popular alternative. I won’t focus on a specific plugin but on the general ideas. It’s worth noting that I have separate workflows for different tasks, and so my vault contains all of these — text, canvas and Excalidraw.</p><p>Next, explore plugins that can add functionality tailored to your needs. There&#39;s a vast array of options, from calendar and task management plugins to those that enhance your writing experience. Don’t be afraid to experiment and find what enhances your workflow the most.</p><p>Perfect your vault by taking the time to create templates for recurring notes. This can streamline your process, making it easier to maintain consistency and focus.</p><p>Lastly, build a connected ecosystem of notes that interact with each other. When your notes interact with one another, you’re no longer dealing with isolated bits of information. Instead, you have a dynamic, interconnected web of knowledge that mirrors the complexity of your thoughts and ideas. This holistic approach to note-taking ensures that your ideas are always accessible, interconnected, and ready to be expanded upon.</p><p>My notes have formed complex connections over time, this diagram shows how my templates link to each other. While this seems like a bit of chaos, it’s actually really helpful to be able to jump between all the relevant pieces of information when viewing a note.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/896/1*HMgaYLxAL-lecAV-BHQW7Q.png" /></figure><h4>Examples</h4><p>In this section, I’ll demonstrate these three aspects through several personal examples, trying to show how they have built up over time.</p><h4>Daily Notes</h4><p>My daily note is the center of my workflow in obsidian. I start each day by opening a new note, and the content is mostly dynamically updated to reflect what happened that day.</p><p>Daily notes are the prime example of a template that needs to be personalized. Not just for every person, but for the way you’re currently using Obsidian. As a concrete example, my daily note has changed significantly when I become a team leader.</p><p>Here’s how the note structure compares:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Jc63pEthDUhiPQRo--Vl-w.png" /></figure><p>All sections are created dynamically with help from plugins such as Dataview, Rollover Daily Todos and QuickAdd.</p><p><strong>Dataview</strong></p><p>This plugin can query your notes and extract specific information out of them. For example, my meeting summary section is a table that is created by querying all the summaries of the meetings that happened today and are in the meetings folder.</p><p><strong>Rollover daily todos</strong></p><p>This nifty little plugin does exactly what it sounds like — it takes all the uncompleted tasks from the previous day and pastes them into the current daily note. This way I don’t have to worry about losing tasks from previous days.</p><p><strong>QuickAdd</strong></p><p>This plugin is all about automating your vault, which I love. It enables you to create macros that run as a command or when pressing a hotkey combination. One of the primary uses I have is for jotting down quick logs of what I’m doing or what I need to remember from anywhere in my vault and having them appear as a list in a section of the daily note I can refer to later.</p><p>While using this extensively as a team member, I’ve realized that as a team lead most of what I need is encapsulated in the meeting summeries and I didn’t use this feature any longer, changing my template to reflect that.</p><p><strong>Meeting Notes</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/246/1*Uz8-kA8DkEr45hPZv2Mu9g.png" /></figure><p>A meeting is a largely structure affair.</p><p>My meeting notes have a few pre-built sections for attendees, points raised during the meeting, tasks, and a summary. Each section was added as I needed it.</p><p>My meeting notes started as plain text files with only titles, but over time I added more advanced features:</p><ol><li>Creating a list of meeting in the daily note, which shows the summary from the metadata</li><li>Creating pages for people, which show the meetings they attended</li><li>Pulling all uncompleted tasks from all meetings so as to never miss a task</li></ol><p>For each of these, I added a small section, slowly building up the form the meeting note has today. This form has been static for a few months now and I’m quite pleased with it.</p><p><strong>Brainstorming \ Planning</strong></p><p>For me, brainstorming is a visual exercise. There are usually multiple components and the interaction between them is what sparks new ideas.</p><p>I could use a text note and references to brainstorm, and there are times that I found that laying out my thought more linearly makes sense and helps me organize them, but for the most part I find that using a visual tool is much more productive.</p><p>You’ve actually seen parts of a brainstorming note already, all the excalidraws were a part of the planning for this post:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*D9CLcy66LWSJSWR8-6TOOQ.png" /></figure><p>My brainstorming notes actually don’t have a template, even though Excalidraw drawings support it. I like the flexibility of the visual method, and the change here happened through using different plugins. I started with Obsidian’s built in canvas, added the X-Mind plugin to it, and only then moved to Excalidraw. I’m still using both, as I have existing brainmaps that I update, but almost all new notes are in Excalidraw.</p><h3>Summary</h3><p>Obsidian is a great tool that can be used in almost endless ways.</p><p>In the fast-paced world of digital note-taking, continuously evolving your Obsidian vault is the key to unlocking its full potential. This post delves into the importance of regularly updating and customizing your vault, transforming it from a static repository into a dynamic, living system. By constantly refining your vault, you ensure that it grows and adapts alongside your ever-changing needs and ideas. From creating interconnected ecosystems of notes to leveraging plugins for enhanced functionality, we’ve seen practical strategies to keep your vault vibrant and relevant.</p><p>I hope that in this post I convinced you that upgrading your obsidian notes a little bit at a time is crucial for maintaining the vault’s value as a productive and helpful tool.</p><p>Have ideas how to make the process better? Want to share your thoughts? Leave a comment so that everyone can benefit!</p><p>Want to see more Obsidian content? Please Clap and follow.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=17b209ad570e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[SurrealKV —Diving deep with the new storage engine in SurrealDB 2.0]]></title>
            <link>https://ori-cohen.medium.com/surrealkv-diving-deep-with-the-new-storage-engine-in-surrealdb-2-0-5c8d276aaaf6?source=rss-1b31f7086ae3------2</link>
            <guid isPermaLink="false">https://medium.com/p/5c8d276aaaf6</guid>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[surrealdb]]></category>
            <category><![CDATA[sql]]></category>
            <category><![CDATA[database]]></category>
            <dc:creator><![CDATA[Ori Cohen]]></dc:creator>
            <pubDate>Thu, 10 Oct 2024 08:56:41 GMT</pubDate>
            <atom:updated>2024-10-10T08:56:41.455Z</atom:updated>
            <content:encoded><![CDATA[<h3>SurrealKV — Diving deep with the new storage engine in SurrealDB 2.0</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ouDiDam_-Pw4FUerYVKPww.jpeg" /><figcaption>Image generated by author using Gemini</figcaption></figure><p><em>Not sure what SurrealDB 2.0 is and what the hype is about? Read my introduction </em><a href="https://ori-cohen.medium.com/surrealdb-2-0-im-excited-let-s-go-a7dea79aae56"><em>here</em></a><em>.</em></p><p>Hidden in SurrealDB’s announcement is a gem that can easily be overlooked — the introduction of a new key-value store, built by the Surreal team, called <a href="https://surrealdb.com/blog/challenge-accepted-announcing-surrealdb-2-0#surrealkv">SurrealKV</a>.</p><p>In this post I’d like to discuss two burning questions I had when reading the post —</p><ol><li><em>Why </em>did they bother building an entire storage engine? (hint — there are a lot of reasons)</li><li><em>How </em>does it work?</li></ol><h3>Why a new engine?</h3><p>SurrealDB uses a key-value (KV) storage under the hood to store and query all of it’s data, including both metadata (e.g. table definitions) and records (the actual rows in the tables).</p><p><em>For a more in depth look on how they do it, have a look at </em><a href="https://medium.com/@ori-cohen/surrealdbs-sophisticated-storage-solution-surrealdb-internals-part-4-29b3ea20e110"><em>this post</em></a><em>.</em></p><p>Previously, SurrealDB had several options for a KV backend, including RocksDB (for local, persistant storage), TiKV (for distributed clusters) and in-memory (for small and/or temporary instances). These seem to account for all major use-cases, why do we need a new one?</p><p>To answer this question we need to look at what a database offers us. On the surface level, a database allows us to simply store and retrieve data, but if we go deeper it can provide so much more. There are several important aspects of a database that are expected of a modern and mature tool:</p><ol><li><strong>ACID </strong>— the ability to perform multiple actions on the database atomically (either all succeed or all fail)</li><li><strong>Versioning </strong>— made a mistake? don’t worry, just see how the data looked in the past. <em>This is a new feature that SurrealKV provides</em></li><li><strong>SurrealDB specific improvements </strong>— by using a data structure uniquely suitable for how SurrealDB stores it’s values, by using an <em>adaptive radix trie.</em></li></ol><p>And finally, one more to align with SurrealDB’s vision — the entire storage engine can be embedded alongside SurrealDB itself for various application needs.</p><h4>Versioning</h4><p>The highlight of the new engine is it’s ability to <em>time travel.</em></p><p>What I mean by that is that it enables querying different <em>versions</em> of the data by specifying the timestamp you want SurrealDB to show you. Querying past versions is extremely useful for many applications: preventing data loss, undoing changes, tracking updates and so on.</p><p>Surreal enables this by introducting a new VERSION keyword that can be added to queries:</p><pre>-- Example from Surreal&#39;s blog post<br>SELECT * FROM user VERSION d&#39;2024-08-12T11:03:00Z&#39;<br>[[{ id: user:john, name: &#39;John v1&#39; }]]</pre><p>Behind the scenes this is accomplished using a <em>versioned trie data structure</em> that allows for querying the same key across different versions. The implementation of this is a whole new project created by the team called <a href="https://github.com/surrealdb/vart">vart</a>, which we’ll explain in the next section.</p><h4>SurrealDB specific improvements</h4><p>I’ve <a href="https://ori-cohen.medium.com/surrealdbs-sophisticated-storage-solution-surrealdb-internals-part-4-29b3ea20e110">written</a> in the past about how SurrealDB stores it’s objects, but the gist of the matter is that it always builds it’s keys as a list of hierarchal values. For example, saving a table will generate a key with the structure:</p><pre>* namespace_name<br>* database_name<br>!tb &lt;- A constant value<br>table_name</pre><p>And creating an object in that table will have a key of the structure:</p><pre>* namespace_name<br>* database_name<br>* table_name<br>* object_key</pre><p>SurrealKV utilizes a prefix-tree (or <em>trie</em>) as the underlying data structure. What this enables is that related objects in the database are “closer” in the stored data. For example, all the objects for the same table have the same prefix (up to the table name), and accessing multiple keys from the same table becomes quicker (you only need to filter for the prefix of the table name once and then you have all the keys).</p><p>By building the new store in such a way, it is uniquely suited for SurrealDB’s use case and can unlock better performance for the entire project.</p><h4>Embedding</h4><p>Lastly, SurrealDB prides itself on being able to be embedded directly, both into other project, on websites or on-device. Building a storage engine that aligns with that vision, can be deployed anywhere SurrealDB is and doesn’t require any external services is a huge milestone that enables SurrealDB to move forwart with their vision of being the new database for the web.</p><h3>How is it built?</h3><p>The second most important questions after <em>why</em> is <em>how</em>.</p><p>In future posts we’ll dive into each element in more detail, but for now we’ll look at a brief overview.</p><p>The use of SurrealKV is done on three layers:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/595/1*J-RyakcOj17E7DpDY-SkUA.png" /></figure><ol><li>SurrealDB translates all the commands it recieves into key-value format, either for saving or retrieving.</li><li>SurrealKV recieves those keys and values and makes sure they are saves in an atomic manner and that no two concurrent actions can impact each other.</li><li>VART is the underlying code that saves the keys as an efficient, versioned <em>Adaptive Radix Trie</em></li></ol><p>All of these layers work together to provide an efficient and reliable database experience.</p><h4>Radix what?</h4><p>The underlying data structure, adaptive radix tree (ART), is a variation on a prefix-tree (or <em>trie</em>), a data structure that allows for efficient storage of lookup of keys. This is doubly so for Surreal, as we’ve seen.</p><p>What ART improves over the classic trie is in two aspects:</p><ol><li>Combining nodes if there is only one child. This way we don’t generate a long length of nodes for an extremely unique key</li><li>Using different sizes of nodes for a different number of child nodes, allowing for optimized storage.</li></ol><p>All in all, ART provides a storage solution that is efficient in both storage space and lookup time. It’s a really cool data structure that I’ll probably do an entire post on in the near future.</p><h3>Summary</h3><p>SurrealDB 2.0 introduces SurrealKV, a new storage engine for the database, that both improves and enables future enhancements to the product,, while introducing two new features: version queries and an embedded store alongside SurrealDB.</p><p>In following posts we’ll be looking more deeply into the technology powering this new engine, I hope you’ll join me on this journey.</p><p>Interested in learning more about how SurrealDB works? All my posts are available <a href="https://ori-cohen.medium.com/list/surrealdb-deep-dive-ddefbefaf1ef">here</a>.</p><p>Have specific questions or requests? Let me know in the comments.</p><p>Please clap and follow for more, I’ll be continuing my deep dives into SurrealKV and SurrealDB.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5c8d276aaaf6" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[SurrealDB 2.0's Brand New Parsing — SurrealDB Internals Part 5]]></title>
            <link>https://ori-cohen.medium.com/surrealdb-2-0s-brand-new-parsing-surrealdb-internals-part-5-60d6cc29256e?source=rss-1b31f7086ae3------2</link>
            <guid isPermaLink="false">https://medium.com/p/60d6cc29256e</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[database]]></category>
            <category><![CDATA[surrealdb]]></category>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[sql]]></category>
            <dc:creator><![CDATA[Ori Cohen]]></dc:creator>
            <pubDate>Wed, 02 Oct 2024 12:02:12 GMT</pubDate>
            <atom:updated>2024-10-02T12:02:12.451Z</atom:updated>
            <content:encoded><![CDATA[<h3>SurrealDB 2.0 New Parsing — SurrealDB Internals Part 5</h3><p><em>Haven’t heard about Surreal’s new major version? Checkout my overview </em><a href="https://medium.com/@ori-cohen/surrealdb-2-0-im-excited-let-s-go-a7dea79aae56"><em>here</em></a><em>.</em></p><p><em>All my SurrealDB posts are available </em><a href="https://medium.com/@ori-cohen/list/surrealdb-deep-dive-ddefbefaf1ef"><em>here</em></a><em>.</em></p><p>In the new version, one of the most important, and in fact the first feature on the <a href="https://surrealdb.com/blog/challenge-accepted-announcing-surrealdb-2-0">blog post</a>, is a brand new parser for SurrealQL, the query language for the database. SurrealQL is a SQL-esque query language, with various features that allow for querying the complex and unique structures SurrealDB provides, such as relations and nested fields.</p><h4>Why the change?</h4><p>The previous version of Surreal used the <a href="https://docs.rs/nom/latest/nom/">nom</a> Rust crate, which allows for building extremely complex parsers by combining smaller functions (for example, a function for identifying the word SELECT and another to identify the name of the columns, and so on). Surreal used this method to parse all SurrealQL queries (for a more in-depth look have a look at my <a href="https://medium.com/@ori-cohen/surrealdb-internal-design-part-3-into-the-rabbit-hole-with-surrealql-c5dc9ef3e119">previous post</a> on the subject).</p><p>This approach, while useful and efficient on it’s own, had several drawbacks which made is less than ideal for Surreal’s use case, as the team explains in the announcement:</p><blockquote>It [the new parser] addresses many of the shortcomings of the previous version, offering <strong>better error messages</strong> and overcoming the limitations of <strong>system stack constraints</strong>, ensuring that performance remains consistent even as query complexity increases … the new parser is an optimised <strong>recursive descent parser</strong> with a <strong>separate lexing</strong> step.</blockquote><p>Whoa, that’s a lot. Let’s break this down.</p><p>The new parser has several important components we’ll dive into:</p><ol><li>Seperating the lexing and parsing steps</li><li>Custom error handling</li><li>Memory management for complex queries</li></ol><p>The way they managed to do all this is by rebuilding the parser from scratch.</p><h4>A Guiding Example</h4><p>Just to have a taste of what this looks like, let’s have a look at a code example (don’t know rust? Don’t worry, everything is explained). I’m using the same example as the <a href="https://medium.com/@ori-cohen/surrealdb-internal-design-part-3-into-the-rabbit-hole-with-surrealql-c5dc9ef3e119">previous post</a> to help comparing the two — the DEFINE TABLE statement, the documentation for which is:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7348ea181544b26b167c22515b3ffad2/href">https://medium.com/media/7348ea181544b26b167c22515b3ffad2/href</a></iframe><p>Now let’s look at the relevant code (don’t worry, no need to know Rust to understand this in a high level):</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b2b873a4861c0e007516f9ba33fbacd6/href">https://medium.com/media/b2b873a4861c0e007516f9ba33fbacd6/href</a></iframe><p>Looking at the code, it’s a pretty straightforward translation to the docs, and the code structure makes it pretty easy to navigate and understand, with each subsection being it’s own separate function. For example, the PERMISSIONS option has a separate function that is called:</p><pre> t!(&quot;PERMISSIONS&quot;) =&gt; {<br>    self.pop_peek();<br>    res.permissions = ctx.run(|ctx| self.parse_permission(ctx, false)).await?;<br>  }</pre><p><em>For those interested in diving deeper, the code for parsing permissions is </em><a href="https://github.com/surrealdb/surrealdb/blob/d4f6d0c311c8aa2d92b7a696df3fa7ea87c3f1dd/core/src/syn/parser/stmt/parts.rs#L274"><em>here</em></a><em>.</em></p><p>Now let’s use the example to understand the changes in this version.</p><h4>Seperate lexing and parsing</h4><p>Almost all common compilers and interpreters nowadays divide the process into two distinct steps:</p><ul><li>Lexing: seperating the text into <em>tokens</em> (usually be whitespace), each token having a meaning in the language.</li><li>Parsing: given a stream of tokens, convert them into an internal representation of the statement that can be acted upon</li></ul><p>Running the lexing step before the actual parsing optimized the parser itself as it doesn’t need to consider irrelevant elements of text (such as whitespaces, linebreaks, indentations etc.) and can focus on the translation itself.</p><p>In our example, the result of separating the lexing is that we don’t need to think about separating the text, we can just ask for tokens, for example:</p><pre> let name = self.next_token_value()?;</pre><h4>Custom Error handling</h4><p>Nom has an adaptive <a href="https://github.com/rust-bakery/nom/blob/main/doc/error_management.md">error handling system</a> that can be used in various cases, but in this case the team probably wanted either more control, an easier way to return human-readable errors, or both.</p><p>The solution is using a custom errors.</p><p>In the example above we abbreviated the error cases, but the function has several places where they are used, for example when parsing the table type:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/927581cd11234e111c6d43c76be55440/href">https://medium.com/media/927581cd11234e111c6d43c76be55440/href</a></iframe><p>The unexpected! macro is a custom macro by SurrealDB that can output the kind of error message we all know and love, such as:</p><pre>&quot;Unexpected token `DUMMY`, expected `NORMAL`, `RELATION`, or `ANY`&quot;</pre><p><em>If you want to dive deeper, the </em><em>unexpected! macro itself is defined </em><a href="https://github.com/surrealdb/surrealdb/blob/d4f6d0c311c8aa2d92b7a696df3fa7ea87c3f1dd/core/src/syn/parser/mac.rs#L10"><em>here</em></a><em>.</em></p><p>The way Surreal utilizes the powerful Rust macros to create a powerful error system while maintaining readably and easy-to-write code is interesting in and of itself, I might have a post on that in the future.</p><h4>Memory management and stack depth</h4><p>The last part of the new parser is the fact that it can better handle more complex queries.</p><p>The previous parser needed to build all possible combinations of the query before parsing, which incurred a heavy cost as queries got more and more complex. This new approach, on the other hand, builds only the necessary parts of the parser that are needed, and discards the parts of the parser that were already run on this query, the result of which is that only the current section being parsed in held in memory, so that the memory footprint remains more or less constant.</p><p>The downside of running extremly deep recursive functions is that the function stack calls (on the stack of the program) can cause the dreaded stack overflow error (the namesake of the famous site). In order to address this issue, they use a rust crate called <a href="https://crates.io/crates/reblessive">reblessive</a>, which creates a dynamic stack on the application heap and calles each recursive call in a seperate future.</p><p>We see this in the premissions example:</p><pre>// ctx in the reblessive stack, which runs the recursive function<br>// as a future that will resolve without overflowing the stack.<br><br>res.permissions = ctx.run(|ctx| self.parse_permission(ctx, false)).await?;<br></pre><p>Using this technique, Surreal minimizes the memory and stack footprint and allows for more complex queries and further flexability in the future.</p><h3>Recap</h3><p>In this post, we briefly looked at the elements that make up the new SurrealQL parser, from the new syntax to the way it better handles memory management.</p><p>This was by no means a comprehensive view, if you’re interested, I encourage you to look at the source code, it is very well organized and easy to navigate.</p><h4>Further reading</h4><p><strong>External crates</strong></p><ol><li><a href="https://crates.io/crates/nom">Nom</a> — the parser library used in the previous versions</li><li><a href="https://crates.io/crates/reblessive">Reblessive</a> — A heap allocated runtime for deeply recursive algorithms.</li></ol><p><strong>SurrealDB</strong></p><ol><li><a href="https://github.com/surrealdb/surrealdb/blob/d4f6d0c311c8aa2d92b7a696df3fa7ea87c3f1dd/core/src/syn/parser/stmt/define.rs#L638">parse_define_table</a></li><li><a href="https://github.com/surrealdb/surrealdb/blob/d4f6d0c311c8aa2d92b7a696df3fa7ea87c3f1dd/core/src/syn/parser/mac.rs#L10">unexpected!</a> macro</li></ol><p><strong>Rust</strong></p><ol><li><a href="https://doc.rust-lang.org/book/ch19-06-macros.html">Macros</a></li><li><a href="https://rust-lang.github.io/async-book/01_getting_started/04_async_await_primer.html">Async/Await</a></li></ol><p>Interested in more SurrealDB deep dives? See all my posts <a href="https://ori-cohen.medium.com/list/surrealdb-deep-dive-ddefbefaf1ef">here</a>.</p><p>Have specific questions or requests? Let me know in the comments.</p><p>Please like and follow for more, I’ll be continuing my deep dives into the new features in version 2.0.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=60d6cc29256e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[SurrealDB 2.0 — I’m Excited, Let’s Go!!]]></title>
            <link>https://ori-cohen.medium.com/surrealdb-2-0-im-excited-let-s-go-a7dea79aae56?source=rss-1b31f7086ae3------2</link>
            <guid isPermaLink="false">https://medium.com/p/a7dea79aae56</guid>
            <category><![CDATA[graphql]]></category>
            <category><![CDATA[surrealdb]]></category>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[database]]></category>
            <dc:creator><![CDATA[Ori Cohen]]></dc:creator>
            <pubDate>Thu, 26 Sep 2024 08:56:06 GMT</pubDate>
            <atom:updated>2024-09-26T08:56:06.849Z</atom:updated>
            <content:encoded><![CDATA[<h3>SurrealDB 2.0 — I’m Excited, Let’s Go!!</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/597/1*Mj3PAiCHT_ATzCoB-I-96g.png" /></figure><p><em>Not sure what SurrealDB is and what the hype is about? Read my introduction </em><a href="https://medium.com/@ori-cohen/surrealdb-internal-design-part-1-welcome-to-wonderland-4444242353e6"><em>here</em></a><em>.</em></p><p>Last week the SurrealDB team announced version 2.0, with major changes encompassing internal changes such as a new parser and a caching layer, as well as exposing new syntax and an experimental GraphQL API. For a full review written by the team you can read their awesome post <a href="https://medium.com/surrealdb/challenge-accepted-announcing-surrealdb-2-0-81a668d6e174">here</a>.</p><p>This version has many amazing features, and instead of running down the list of features like they did, I would like to discuss how the features show how the project is becoming more mature and why I think this marks the beginning of the tool becoming more generally accepted.</p><p>I will categorize the features into two groups:</p><ul><li>Changing Surreal to use more industry standards</li><li>Addressing the community’s major pain points of performance and snapshots</li></ul><h3>Welcoming existing standards</h3><p>For a long time, SurrealDB was the king of inventing new concepts. This was not like any other database anyone has ever seen. This had both positive and negative implications. On one hand — people were excited to be able to model more complex data in a simpler way with powerful querying syntax. On the other hand, however, the learning curve for Surreal was quite steep. You had to learn both the data model and the query language before being able to work properly with the DB, which put off many.</p><p>In this release, Surreal’s direction moves more into industry standards that all of us working with data have come to both accept and love. In my opinion, the feature that shows this most clearly is the GraphQL API, but it also shines through many of the smaller features as well.</p><h4>GraphQL</h4><p>Since it’s invention, <a href="https://graphql.org/">GraphQL</a> has quickly become one of the world’s favorite API standards, one of the main reasons being the fact that it moves the power into the hands of the data consumers (be it the frontend site or different services) by enabling each consumer to request exactly the data it needs.</p><p>This reason is also why this is such a good fit for SurrealDB — which aims to be an all-in-one backend solution. By welcoming GraphQL with open arms, Surreal leaps forward in several key aspects:</p><ol><li><strong>The learning curve dramatically decreases</strong>: GraphQL’s language is more generally known and is less complex than SurrealQL’s more niche features.</li><li><strong>SurrealDB can now be a drop-in replacement! </strong>I can’t understate the importance of this. The hardest part of using a new technology is that fact that our application usually have existing code to request data. For many, this is GraphQL, which means adopting SurrealDB into new projects has never been easier!</li></ol><h4>Other Features</h4><p>Many other features also nod to the fact that Surreal is accepting the standard terminology, making it easier for experienced data practitioners to use the project. I’ll quickly go through them:</p><ol><li><a href="https://surrealdb.com/docs/surrealql/statements/define/access">Define access</a> moves to a more accepted <em>access</em> terms instead of Surreal’s <em>tokens. </em>The new access statement also accepts known methods such as JWT.</li><li><a href="https://surrealdb.com/docs/surrealql/statements/define/field#defining-a-literal-on-a-field">Nested field definition as literals</a>. Experience teaches us that the more strict the data validation, the less bugs we’ll have. Enabling people to define complex types while maintaining strong typing makes the usage much more similar to any type-safe language or DB.</li><li><a href="https://surrealdb.com/docs/surrealql/statements/upsert">Making UPSERT explicit</a>. While in previous version UPDATE could also insert new data, in this version the UPDATE statement acts like it does in any other database and in order to create or update we need to explicitly declare we wish to do it. This will reduce bugs for people who come from other database solutions by a significant margin.</li></ol><p>All of these features show a direction, in my opinion, that will make SurrealDB a more widespread and accepted solution.</p><h3>User pain points</h3><p>The second important aspect of this version is that it solves many of the major pain points users had head on.</p><h4>Performance</h4><p>One of the most common complaints about Surreal is that it sacrificed speed for flexibility, with user complaining it is slower that classical DBs such as Postgres. While not publicly available yet (they are now <a href="https://github.com/orgs/surrealdb/discussions/43#discussioncomment-10692831">working</a> on making them stable), many features in this release aim to make SurrealDB faster and more reliable. Let’s look at some of them:</p><ol><li><strong>New parser: </strong>I’ve <a href="https://medium.com/@ori-cohen/surrealdb-internal-design-part-3-into-the-rabbit-hole-with-surrealql-c5dc9ef3e119">previously covered</a> the way SurrealQL has been parsed in the previous version. The parsing has been rebuilt from the ground up to be more efficient and more flexible. I’ll write a more in-depth post on the subject in the near future.</li><li><strong>A new caching layer</strong> for faster queries without multiple round-trips to the underlying storage.</li><li><strong>Better Indices, </strong>that<strong> </strong>now have better algorithms, <a href="https://surrealdb.com/docs/surrealql/statements/define/indexes#using-concurrently-clause">build asynchronously </a>and can be <a href="https://surrealdb.com/docs/surrealql/statements/rebuild">rebuilt</a>, all of which allows for faster queries on more data.</li></ol><h4>Snapshots</h4><p>Ever accidentally deleted important data? Updated a field you shouldn’t have? Many databases have a concept of <em>versioning</em>, which allows querying previous iterations of the data. SurrealDB now joins the club by introducing their own storage solution — SurrealKV.</p><p>SurralKV is a replacement for the previous storage layers (RocksDB, TiKV, etc.) and allows for snapshot queries, which means you can see how the data looked on a specific time.</p><h3>Conclusion</h3><p>I see this version as a first huge step in a good direction.</p><p>This version showed how Surreal can balance both it’s own identity and the world’s best practices. I believe in the coming months and years we’ll see the product mature and become a product we can all look forward to using.</p><p>Interested in more SurrealDB deep dives? See all my posts <a href="https://ori-cohen.medium.com/list/surrealdb-deep-dive-ddefbefaf1ef">here</a>.</p><p>Please like and follow for more, I’ll be continuing my deep dives into the new features in version 2.0.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a7dea79aae56" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Three Lessons Learned as a Team Lead, for a Better Future]]></title>
            <link>https://ori-cohen.medium.com/three-lessons-learned-as-a-team-lead-for-a-better-future-823c7338bbb6?source=rss-1b31f7086ae3------2</link>
            <guid isPermaLink="false">https://medium.com/p/823c7338bbb6</guid>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[data]]></category>
            <category><![CDATA[team-leader]]></category>
            <category><![CDATA[tech-team]]></category>
            <category><![CDATA[management]]></category>
            <dc:creator><![CDATA[Ori Cohen]]></dc:creator>
            <pubDate>Fri, 17 Mar 2023 12:30:37 GMT</pubDate>
            <atom:updated>2023-03-17T12:30:37.347Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/512/1*6X8a-xbelDMxiaRg3YHvqg.png" /><figcaption>“a software team leader looking into a better future, sitting in front of a laptop and thinking, art” (generated by stable diffusion)</figcaption></figure><p>For the last two years, I’ve been the team lead for a team in a large organization. During this time, I’d devoted a considerable amount of time to building a framework of principles that guide me in my job as both a manager, a friend and a member of the organization who is committed to it’s continued growth and success.</p><p>This article will focus on three points that I believe are the most relevant and that I wish someone had discussed with me when I entered my position. The article is meant not only for team leads, but also towards anyone who would like to take a step back and consider how, perhaps, we can create a work environment that is both better to work in and is more productive.</p><p>I am always looking to learn, as so feel free to comment your thoughts on the matter.</p><p>Without further ado, let’s get into it.</p><h3>Ego</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gQWhK68mYMVtgmD8e7iZXA.png" /><figcaption>Photo by mariel reiser on Unsplash</figcaption></figure><p>Ego, in the sense that I want to talk about, is “an inflated feeling of pride in your superiority to others” (vocabulary.com).</p><p>Everyone has some ego, and while it can be a driving force for positive change, it can often be a great hindrance to mutual projects, as people seek to put themselves above the shared goal. As a team lead, one of my main jobs has been to work both with my team members and other coworkers, while carefully balancing the need to provide value with the need to make the workers feel fulfilled, so as to not let everyone’s ego get in our way.</p><p>I’ve had projects go either way. I’ve had projects where all participants were entirely on board and completely dedicated to our work, as well as less successful projects where the ego of other teams didn’t let us move forwards in our own projects.</p><p>There are a few methods that can improve the situation:</p><ol><li><strong>Convince the upper management of a shared goal.</strong> Higher mangers are a resources I wish I’d employed more earlier. They can negotiate on your behalf, find resources and compromises that I could not, and back you up when you make your case (especially when you have data to show, read on for more on the that subject).</li><li><strong>Remove toxic team members,</strong> either entirely or from a specific project. There are times where one or two people are the catalysts for the friction in the project, and so removing them from the work will do wonders.</li><li><strong>Invest time into personal meeting,</strong> both inside your team and outside of it, so that you can discuss this issue frankly. From my experience, people would like to improve, and constructive feedback on this subject can go a long way</li></ol><h3>Separation of concerns</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xwMoDzpumOQUxea-KP_6Lg.png" /><figcaption>Photo by Jens Lelie on Unsplash</figcaption></figure><p>Often times, the team lead has two roles, both of which are critical to the team’s success:</p><ol><li>The Manager — a person responsible for directing the team and organizing both the technical, logistical and administrative work</li><li>The Tech Lead — the most knowledgeable person on the team that oftentimes also has legacy knowledge about past team activities.</li></ol><p>The reason for this is clear and perfectly reasonable. Oftentimes, one of the senior team members is the one promoted to management, and it is only natural to put him or her in charge of the team they know best. Nevertheless, this put the team in it’s entirety in a fragile state that I believe should be avoided and worked upon.</p><p>This state stems from the fact that a team cannot depend on a single person, especially the team lead. As a lead, I find that most of my time is spent in meeting, both inside the team and outside of it, and I am therefor not free to help on the technical issues the team faces. This has gotten to a point where when I take a vacation, the team’s ability to solve issues is noticeably degraded.</p><p>I have taken steps to remedy this situation, investing considerable time and effort into the process. Here are a few tips how to do so in your own team:</p><ul><li><strong>Let the team make technical decisions</strong>. I have met many leads who want to make all decisions, down to the technical details. Having the team make the decisions, on the other hand, both ensures the development of team members, gives them the satisfaction of knowing they are responsible for their entire project, and improve the technical knowledge of the entire team.</li><li><strong>Have a tech lead\architect role in your team, </strong>both as a means of promoting a successful team member and ensuring the longevity of the team’s knowledge. The way to create such a role is by having a senior teammate start attending more technical meetings, until they have a broad enough understanding of the team’s projects that they can support the team as a technical knowledge base.</li><li><strong>Give your team the freedom to make mistakes</strong>. As a lead, the most important currency I can invest is time (both mine and the team’s), and letting the team know I choose to invest our time in the way they choose and that I have their back if we need to change in the future is the way I found most enhances the teammates’ will to expand their knowledge.</li><li><strong>Give teammates a broad view.</strong> In some team, the workers focus entirely on the technical job at hand, while the lead also is responsible for coordinating with other teams, the product owners, the IT devs and so on. I found that letting each team member have a complete, end to end, ownership of their projects (from research to coding to monitoring to deploying) lets them become better at both their job and as teammates.</li></ul><h3>Start Speaking the Data Language</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UG2P1g_Ou_hz53FVNSt95A.png" /><figcaption>Photo by Carlos Muza on Unsplash</figcaption></figure><p>We’re living in 2023, there is absolutely no reason to keep arguing without data to back up our claims. As a team lead, I found that bringing facts is one of the most reliable ways to convince people, both managers and fellow leads.</p><p>Put some time into building data infrastructure in your team, it will benefit you in the long run. Some of the features may include:</p><ol><li><strong>A data map, </strong>such as <a href="https://open-metadata.org/">OpenMetadata</a>, that allows you to clearly define where your data exists, what it contains and how to access it.</li><li><strong>A dashboard system, </strong>such as Redash, that allows your to show any and all interested parties insights regading your team in a user-friendly visual interface. I have also found that it is mostly an investment that is very beneficial in the long run, as it frees the team from constant report generation.</li><li><strong>Well defined logs and metrics</strong>. Having the state of your systems explained well, both their health and the value they bring, is a huge asset.</li></ol><p>We team leaders are investors no less than any VC. We invest the time and resources of our team, and we need to do so wisely and carefully.</p><p>I hope that I convinced you that you should invest some of your time into making the people under you better and enhancing the data infrastructure, both long term investments that together, I believe, will let us create a better tomorrow.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=823c7338bbb6" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[SurrealDB’s Sophisticated Storage Solution — SurrealDB Internals Part 4]]></title>
            <link>https://ori-cohen.medium.com/surrealdbs-sophisticated-storage-solution-surrealdb-internals-part-4-29b3ea20e110?source=rss-1b31f7086ae3------2</link>
            <guid isPermaLink="false">https://medium.com/p/29b3ea20e110</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[surrealdb]]></category>
            <category><![CDATA[database]]></category>
            <category><![CDATA[open-source]]></category>
            <dc:creator><![CDATA[Ori Cohen]]></dc:creator>
            <pubDate>Sat, 12 Nov 2022 16:56:12 GMT</pubDate>
            <atom:updated>2022-11-12T16:59:18.367Z</atom:updated>
            <content:encoded><![CDATA[<h3>SurrealDB’s Sophisticated Storage Solution — SurrealDB Internals Part 4</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*87fKwy-CdysmzVy_M-yPdg.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/es/@calvinhanson?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Calvin Hanson</a> on <a href="https://unsplash.com/s/photos/key?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p><em>Hot tip: All stories in this series are </em><a href="https://medium.com/@ori-cohen/list/surrealdb-deep-dive-ddefbefaf1ef"><em>here</em></a></p><p>There is an old programming saying that it is usually better to have simple code that is easily explainable than obscure code that is more performant. This is definitely one of the exceptions.</p><p>The only way I know how to describe the way Surreal stores it’s data is as brilliant, simply brilliant.</p><p>In order to understand how the data is saved, let’s first look at what <em>needs</em> to be in storage. There are two categories of items to store:</p><ol><li>Metadata — the structure of tables, indices, scopes, etc.</li><li>Data — the values of the objects retained by Surreal</li></ol><p>As the underlying storage is a key-value store, both are stored in a similar way, but the values stored for each are different.<br>In order to better demonstrate how Surreal stores data, I built a <a href="https://github.com/oricc/surrealdb_rocksdb_viewer">small application</a> to read the RocksDB file that Surreal stores it’s data in (if the server is started with a file path).</p><h4>Running the RocksDB Viewer</h4><p>First, we start both the server and SQL prompt:</p><pre>surreal start rocksdb://local.db --user root --pass root</pre><pre>surreal sql --conn http://0.0.0.0:8000 --user root --pass root --ns test --db test</pre><p>Second, we insert some data:</p><pre>-- create new tables and records<br>CREATE user:oricc SET name=&quot;Ori&quot;;<br>CREATE user:tobie SET name=&quot;Tobie&quot;;<br>CREATE repo:surrealdb SET name=&quot;SurrealDB&quot;;<br><br>-- Add some relations<br>RELATE user:tobie-&gt;contributes-&gt;repo:surrealdb;<br>RELATE user:ori-&gt;contributes-&gt;repo:surrealdb;</pre><p>then, when we run the app, we get the following results (the left column is the key, the right is the value):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2L3W1E_ZrCzY9NUi8VZF5A.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*v6j3gI-yB1bp-pETA-SMKg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*48iPOtYIgU-TuQZZs7hqLQ.png" /></figure><p>Let’s break down what we see, starting with the metadata.</p><p>The first rows describe the objects in the database:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/528/1*mT7Yz_aqP2TRqbxxumqtyw.png" /></figure><p>Surreal has several types of objects, here we can see the namespace, database, and tables that are defined in this server.</p><p>Now the question becomes what the keys and values represent. In order to understand that, we’ll go back to the code.</p><h4>Understanding Keys</h4><p>Surreal has an entire directory (<a href="https://github.com/surrealdb/surrealdb/tree/main/lib/src/key">lib/src/key</a>) which deals with creating the keys for the various different objects. For this example, we’ll look at our old friend, the table.</p><p>The struct defining the table key has the following structure:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/af4fb46399d364fdcbbae4fbe75c6338/href">https://medium.com/media/af4fb46399d364fdcbbae4fbe75c6338/href</a></iframe><p>At first, this struct looks very odd. What are all those variables with the unconventional names? what are we representing here? Look closer, does it look familiar? Let me give you another hint.</p><p>When creating the key it’s values are initiated as follows:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0863e766592cd7116138008e00837733/href">https://medium.com/media/0863e766592cd7116138008e00837733/href</a></iframe><p>Yes! This struct is directly mapped to the key for the tables that we can see in the image above!</p><p>How the struct is turned into the key is another interesting point. Notice in the struct definition, the struct <em>macro-derives </em>the <em>Key</em> macro, which automatically creates the serialization and deserialization methods for the struct using the msgpack format. <a href="https://msgpack.org/">MessagePack</a> is a protocol for efficiently compressing data into a minimalist format, Surreal uses the serde binding for the protocol (<a href="https://docs.rs/rmp-serde/latest/rmp_serde/">rmp_serde</a>) so that structs can be easily mapped to and from the serialized data. For those interested in the macro itself, you can have a look at <a href="https://github.com/surrealdb/surrealdb-derive/blob/main/src/lib.rs#L50">the code</a>.</p><h4>Understanding Values</h4><p>The second element of the metadata is the values.</p><p>In order to understand what the value of the metadata stores we need to look back to the previous post and review the table definition statement:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/fc7b62cd146db0b803e085737c1a3541/href">https://medium.com/media/fc7b62cd146db0b803e085737c1a3541/href</a></iframe><p>Now compare the struct to the value for the “contributes” table:</p><pre>��contributes�����None�None�None�None</pre><p>This is an exact map of the table definition, again using the <em>Store</em> macro to create the MessagePack methods on the struct that translate it to the value we see in the RocksDB.</p><p>What this allows us to do is to always have the entire definition for each of the objects in the database, and act accordingly.</p><h4>Item storage</h4><p>For any non-metadata items we need to save in the key-value store, we can easily see that they are very similar to the metadata:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/722/1*Gacja8a5gb5hwVpSZRP_Cw.png" /></figure><p>Each item is stored with the entire hierarchy (Namespace -&gt; Database -&gt; table -&gt; ID) and the value is the MessagePacked object itself (notice the <em>name </em>and <em>Ori</em> inside the value).</p><p>That’s it for storing the values themselves.</p><p>The next question is why the keys have this format.</p><h3>Breaking down the key structure</h3><p>The structure of the keys is actually the reason this storage design is so brilliant. In order to understand why, we first need to understand how access to key-value databases is done. There are two methods of accessing a KV store.</p><ol><li>Key-based — we look up a specific key and get the value for it</li><li>Scan — we look up a range of keys and retrieve all the values.</li></ol><p>By design, key-based lookups are extremely efficient, while scans are a little slower. For that reason, we want to have the range we are scanning be as small as possible.</p><p>The way Surreal achieves this is by building the keys in a hierarchy that allows us to translate each key <em>type </em>into a key range in the underlying KV storage.</p><p>Let’s go back to our trusty example — the table.</p><p>As we saw, the table name is a concatenation of the namespace, database and table name, <em>in this order. </em>Therefor, if we want to find all tables in the <em>test</em> namespace and <em>test</em> database, we would look for all keys of the form <em>/*test*test!tb[</em><strong><em>table_name</em></strong><em>] . </em>This can be translated into a range by looking for any character where the table name should be. In technical terms this means looking for all hex values from <strong>0x00</strong> to <strong>0xff.</strong></p><p>This is exactly what Surreal does!</p><p>Each item (table, fields, etc.) has two functions called <em>prefix</em> and <em>suffix </em>that allow Surreal to map the ranges for this item.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d049bca5131cb541ba01b5d07b353441/href">https://medium.com/media/d049bca5131cb541ba01b5d07b353441/href</a></iframe><p>These functions are used to iterate the range and find all relevant keys (for example, where querying the info for a database).</p><p>This same method is used to query all records of a table (because the record keys can be iterated given the table name), <strong>and is the base that makes Surreal so fast!</strong></p><h3>Conclusion</h3><p>Whoa! We looked at a lot of details under-the-hood today.</p><p>Let’s recap:</p><ol><li>Surreal stores all data (both the database structure and the data itself) in a Key-Value store</li><li>The keys are built using a concatenation of all the previous database layers (for example, table keys include the database and namespace)</li><li>This structure allows Surreal to iterate over all the items inside an object (e.g. tables in a database or rows in a table)</li><li>The values are a serialized using MessagePack in order to save storage.</li></ol><p>This is the heart and soul of Surreal, and is the reason Surreal is both so flexible and so performant, both of which make SurrealDB such an innovative project and a database for the future.</p><p>Want to read more? All SurrealDB stories are available <a href="https://medium.com/@ori-cohen/list/surrealdb-deep-dive-ddefbefaf1ef">here</a>.</p><p>Please clap if you liked the content and follow for more content. Let me know in the comments what else you would like a deep dive into.</p><p>See you next time!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=29b3ea20e110" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[SurrealDB Internal design — Part 3— Into the Rabbit Hole with SurrealQL]]></title>
            <link>https://ori-cohen.medium.com/surrealdb-internal-design-part-3-into-the-rabbit-hole-with-surrealql-c5dc9ef3e119?source=rss-1b31f7086ae3------2</link>
            <guid isPermaLink="false">https://medium.com/p/c5dc9ef3e119</guid>
            <category><![CDATA[sql]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[database]]></category>
            <category><![CDATA[surrealdb]]></category>
            <dc:creator><![CDATA[Ori Cohen]]></dc:creator>
            <pubDate>Tue, 18 Oct 2022 18:25:01 GMT</pubDate>
            <atom:updated>2022-10-18T18:31:52.088Z</atom:updated>
            <content:encoded><![CDATA[<h3>SurrealDB Internal design — Part 3— Into the Rabbit Hole with SurrealQL</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lYHnj517cbAkMi5vgj-wwQ.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@sigmund?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Sigmund</a> on <a href="https://unsplash.com/s/photos/lego-bricks?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p><em>A full list of posts in the series can be found </em><a href="https://ori-cohen.medium.com/list/surrealdb-deep-dive-ddefbefaf1ef"><em>here</em></a><em>.</em></p><p>In the previous post we looked at an overview of SurrealDB and it’s architecture. In this post we will start looking under the hood of SurrealDB — with the code responsible for parsing the language Surreal speaks, which is commonly referred to as SurQL (Pronounced as <em>circle</em>).</p><h3>SurQL</h3><p>The language itself is similar to classical SQL in structure, but it has a few unique features to allow for SurrealDB’s full abilities.</p><p>The way Surreal parses the queries passed to it is by using the <a href="https://github.com/Geal/nom">nom</a> crate. Nom is a parsing library that allows for building parsers incrementally by chaining functions, while also using Rust’s strong type system to maintain the memory and type safety Rust guarantees as part of it’s programming model.</p><p>Basically, you can write functions to parse small pieces of text that return a Rust type, and compose them in larger and larger functions that eventually can parse your entire language, much like building up with Lego pieces into a larger and larger build.</p><p>Let’s look at an example from inside Surreal itself — the table definition.</p><p>As with any Rust code, the data type is the language we build our code around. Let’s start by looking at the table definition type.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/fc7b62cd146db0b803e085737c1a3541/href">https://medium.com/media/fc7b62cd146db0b803e085737c1a3541/href</a></iframe><p>Just by looking at the type we can understand a lot about the code that we need to write. We will need internal functions to parse sub-types (<em>Ident, View </em>and <em>Permissions</em>). As well as seeing the view definition is optional, and we will need to handle the cases where it does and doesn&#39;t exist separately.</p><p>For reference, the <a href="https://surrealdb.com/docs/surrealql/statements/define">official docs</a> give us the full syntax (abbreviated here to only show the table definition):</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b4bd7c0e74195392d9e9632f1642afa1/href">https://medium.com/media/b4bd7c0e74195392d9e9632f1642afa1/href</a></iframe><p>We can see that the type and syntax overlap exactly, but to me the type is easier to understand as it destructures the statement into it’s components in a clear and concise way.</p><p>First, let’s try and describe the statement as a tree of statements. The beauty of nom is that it allows us to write each leaf in the tree as a separate parser and then combine them all into a large parser.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/843/1*rkJ2uDIDNywX7w01Wt6Ueg.png" /><figcaption>Define Table Statement Parser Diagram</figcaption></figure><p>The translation into code is straightforwards:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/65f7a3e167c1b4aa89137032f2aaae63/href">https://medium.com/media/65f7a3e167c1b4aa89137032f2aaae63/href</a></iframe><p>The function itself is a bit long, but it shows us exactly how awesome Nom is.</p><p>Let’s start from the top:</p><pre>fn table(i: &amp;str) -&gt; IResult&lt;&amp;str, DefineTableStatement&gt; </pre><p>Each parser in Nom takes the string to parse as an input, and returns a special type of Result (as the parsing can fail). When the parsing succeeds, the parser returns a tuple — both the remaining text to process (and that text is passed on to additional functions) and the parsed Rust type that is created from this parser, in our case the table definition.</p><p>The parsing itself is done in only 6 lines of this function (lines 4–9), which also demonstrate how to use the result of a parser, for example (line 8):</p><pre>let (i, name) = ident(i)?;</pre><ul><li><em>i — </em>the remaining text</li><li><em>name</em> — the object of type <em>Ident</em> that the parser returned</li></ul><p>Reading the parsing lines we can easily understand the allowed syntax for this command.</p><p>Lines 12–43 show us the extreme usefulness of the fact that Nom returns rust types. In those lines we are using idiomatic Rust, and are guaranteed all the memory safety as well as correctness promises Rust gives.</p><p>In order to gain a better understanding of the power of composite parsers, let’s dive even deeper into the table options parser.</p><p>Again, the options themselves are guaranteed to be a certain set of commands via a use of a Rust type:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8779e392039b72f088f5d8d1aeb2298b/href">https://medium.com/media/8779e392039b72f088f5d8d1aeb2298b/href</a></iframe><p>The really interesting part comes at the parsing stage. As we can have multiple options that come in different orders, we need to check for <em>alternatives, </em>each alternative a separate parser. Luckily, nom makes this almost trivial.</p><p>First we define a series of parsers, one for each option (notice that all return a variant of the enum we defined above):</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3c948bb341cb9bd77358fdbc1a436b63/href">https://medium.com/media/3c948bb341cb9bd77358fdbc1a436b63/href</a></iframe><p>And then we simply wrap them is a general parser for all options that can return any of the alternatives, using the build in <em>alt</em> parser nom provides, which does exactly what we want:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/bc3e4a9d286b2ede148b0816b10bc2e5/href">https://medium.com/media/bc3e4a9d286b2ede148b0816b10bc2e5/href</a></iframe><blockquote>For those interested, a good exercise is to look even further down at the <a href="https://github.com/surrealdb/surrealdb/blob/main/lib/src/sql/view.rs#L34">view parser</a> and understands how it fits in to the general table definition parser.</blockquote><p>But wait, we are not done. How do we get <em>all</em> the options for the table? Nom again comes to the rescue!</p><p>Look again at the definition parser:</p><pre>let (i, opts) = many0(table_opts)(i)?;</pre><p>We used another nom parser called <em>many0</em>, which allows us to iterate and return a list (of type <em>Vec&lt;DefineTableOption&gt;</em>) of all the matching patterns in a sequence until the match fails, allowing for the option that there are no matches (the complementary <em>many1 </em>method requires at least one match or the entire parser will fail).</p><p>All parsers in the project follow the same pattern, combining smaller parsers into increasingly large ones that can deal with the large and complex statements that make up SurQL.</p><h3>Conclusion</h3><p>In this post we looked at how SurrealDB uses Nom to parse SurQL, it’s SQL-like query language, using the example of the table definition to study the amazing power of combination parsers to create complex ones with relative ease.</p><p>Now that you understand one, understanding all other parsers in SurrealDB should not be a problem. In addition, here are a few tips to not get lost in the depth of the parsers:</p><ol><li>Start with the structs, they will tell you what should be returned as the entire statement and allow you to get a overhead view of the parser.</li><li>Open the docs, if they exist, to see how the parser translates into the full query.</li><li>don’t worry if you don’t understand a parser all the way down, sometimes knowing the type returned from some sub-parser (for example, the <em>view</em> parser above) is enough.</li></ol><p>In the next post we’ll talk about the core of SurrealDB and one of the most brilliant and exciting parts of it — the data model in the Key-Value stores backing the database.</p><p>See you then!</p><h4>Additional Resources</h4><ul><li>A great explanation about nom parsers: <a href="https://naiveai.hashnode.dev/practical-parsing-nom">https://naiveai.hashnode.dev/practical-parsing-nom</a></li><li>Nom repo: <a href="https://github.com/Geal/nom">https://github.com/Geal/nom</a></li><li>SurQL in the official docs: <a href="https://surrealdb.com/docs/surrealql/statements">https://surrealdb.com/docs/surrealql/statements</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c5dc9ef3e119" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[SurrealDB Internal design — Part 2 — Architecture]]></title>
            <link>https://ori-cohen.medium.com/surrealdb-internal-design-part-2-architecture-1d816dfd62ee?source=rss-1b31f7086ae3------2</link>
            <guid isPermaLink="false">https://medium.com/p/1d816dfd62ee</guid>
            <category><![CDATA[surrealdb]]></category>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[database]]></category>
            <category><![CDATA[code-architecture]]></category>
            <category><![CDATA[designcode]]></category>
            <dc:creator><![CDATA[Ori Cohen]]></dc:creator>
            <pubDate>Fri, 14 Oct 2022 08:04:00 GMT</pubDate>
            <atom:updated>2022-10-18T18:31:27.113Z</atom:updated>
            <content:encoded><![CDATA[<h3>SurrealDB Internal design — Part 2 — Architecture</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HADBzo4WK7Dbl1dZ9Vc8jw.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@5tep5?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Aleksandr Popov</a> on <a href="https://unsplash.com/s/photos/code-architecture?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p><em>A full list of posts in the series can be found </em><a href="https://ori-cohen.medium.com/list/surrealdb-deep-dive-ddefbefaf1ef"><em>here</em></a><em>.</em></p><p>In the <a href="https://link.medium.com/IduaWxC25tb">previous post</a> we talked about the features of SurrealDB, in this post we’ll start looking at the code itself from the top down.</p><p>We’ll start with the general architecture design and then look at the file structure of the project.</p><h3>Architecture</h3><p>SurrealDB is built with one of the most time-tested and well-known architectures for any project and for servers in particular — the 2-tier architecture.</p><h4>2-Tier Architecture</h4><p>This architecture is based on the idea that we need to separate our code into two logical parts (called layers):</p><ol><li><strong>API</strong> (Application Programming Interface)— the client facing part of the application</li><li><strong>DAL</strong> (Data Access Layer)— the part of the code that handles our custom code and talks to external systems</li></ol><p>What separates these two layers is the <em>language</em> they speak. What I mean by this is that each layer uses different data models:</p><ol><li>The <strong>API </strong>talks in the language of the users, and translates it to the internal models</li><li>The <strong>Business Logic (BL) </strong>translates our models and communicates with external services using <em>their language, r</em>eturning the results back to the API layers using the internal models</li></ol><p>Each tier only calls the tier below it and doesn’t know about the other tiers.</p><h4>SurrealDB Architecture and File Structure</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/762/1*T9F3s4_tmxU7pdVfO3DRfw.png" /></figure><p>SurrealDB’s implementation of the 2-tier architecture is shown in it’s file structure.</p><pre>|-- src            // API layer<br>    |-- net       <br>    |-- cli<br>    |-- rpc<br>    ...</pre><pre>|-- lib/src       // BL<br>   |-- sql <br>   |-- fnc<br>   ...</pre><pre>   |-- kvs  <br>      |-- indexdb<br>      |-- rocksdb<br>      |-- tikv<br>      ...<br>   </pre><p>The <a href="https://github.com/surrealdb/surrealdb/tree/main/src">src</a> folder includes all the API code, each subfolder representing a different way to access the DB (REST, websockets, CLI, etc…)</p><p>The <a href="https://github.com/surrealdb/surrealdb/tree/main/lib/src">lib</a> folder includes both the BL.</p><ul><li>The <a href="https://github.com/surrealdb/surrealdb/tree/main/lib/src/kvs">kvs</a> subfolder includes all the different storage options for the data (memory, TiKV, RocksDB, etc…)</li><li>The rest of the subfolders are the business logic, the custom code the makes SurrealDB function</li></ul><p>Knowing the file structure of the project and understanding how the different parts talk to each other is always the best way to getting started on a new project, as it allows us to start jumping around the code base without getting lost.</p><h3>Code Examples</h3><p>Let’s look at some examples from the code itself.</p><h4>API Layer</h4><p>As we said previously, the API is responsible for communicating with the clients, and allowing the internal code to run without knowing the origin of the request, allowing it to be easily switched out. This is the reason SurrealDB can be used in several modes, including both as a server and as an embedded database.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/87143f916e8c742752c4611016b51495/href">https://medium.com/media/87143f916e8c742752c4611016b51495/href</a></iframe><p>The heart of this function is line 15, which calls the method of the BL that runs the operations requested by the user. The rest of the code wraps around this line and translates the user language into the server’s language and back into the user response format.</p><h4>Business and Data Layer</h4><p>The data layer both parses runs the application code and accesses the underlying data itself.</p><p>In SurrealDB, each file in <em>lib</em> is responsible for both the parsing and computing of a specific functionality. For example, the select statement has it’s own file, all the code related to the geometry is in another, etc.</p><p>Let’s look for example at defining a database.</p><p>We will have a more in-depth look at the query language itself in the next post, but let’s look at the example in the context of the layer architecture.</p><p>The code to handle the database definition is divided into four parts:</p><ol><li>Model — The internal model that the database knows how to handle</li><li>Parsing — The method that translates the user’s language (SurQL) into the internal model</li><li>Display Format — For displaying the internal model in the user’s language</li><li>Compute — Finally, the method to execute the code represented in the internal model</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/759/1*9gwiAQ1mTvmAhS-3QoFDMg.png" /></figure><p>As you can see, these functions can be seen as <em>translations </em>between the different languages and between the different layers.</p><p>Now that we understand the high-level architecture, we can look at the code itself.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/44956ac65d72351a0888c53b3d03c091/href">https://medium.com/media/44956ac65d72351a0888c53b3d03c091/href</a></iframe><h3>Conclusion</h3><p>See, not so bad after all, if we just know how each piece fits into the larger architecture.</p><p>Now that we understand the high-level overview of the code, in the following posts we will dive into one subject at a time and look at the internal implementation.</p><p><strong>Next up </strong>— understanding how Surreal parses the query language (we’ve seen a small example above).</p><p>See you there!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1d816dfd62ee" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[SurrealDB Internal design — Part 1 — Welcome to Wonderland]]></title>
            <link>https://ori-cohen.medium.com/surrealdb-internal-design-part-1-welcome-to-wonderland-4444242353e6?source=rss-1b31f7086ae3------2</link>
            <guid isPermaLink="false">https://medium.com/p/4444242353e6</guid>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[surrealdb]]></category>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[database]]></category>
            <category><![CDATA[code]]></category>
            <dc:creator><![CDATA[Ori Cohen]]></dc:creator>
            <pubDate>Sun, 09 Oct 2022 09:35:49 GMT</pubDate>
            <atom:updated>2022-10-18T18:31:12.482Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="The SurrealDB logo" src="https://cdn-images-1.medium.com/max/864/1*0WNVU1HfJvNIGwkhG274Qw.png" /><figcaption>SurrealDB logo (source: <a href="https://surrealdb.com/static/img/home/logo@2x-d163555351df26f3481b09211b6ef843.png">docs</a>)</figcaption></figure><p><em>A full list of posts in the series can be found </em><a href="https://ori-cohen.medium.com/list/surrealdb-deep-dive-ddefbefaf1ef"><em>here</em></a><em>.</em></p><p>One of the most hyped and anticipated Database technologies that has arisen recently is a product created by two individuals and that promises to be the DB for the future — that is none other than <a href="https://surrealdb.com/">Surrealdb</a>.</p><p>The code was open sourced a few months ago, and I’m proud to say I contributed a feature to this amazing project. What I’ve discovered while writing code in this ecosystem, is that the internal design of Surrealdb is not documented, and so I had to figure out where anything goes by debugging and clicking references in VSCode, not an ideal situation by any measure.</p><p>As an attempt to make the code more accessible to the community as a whole, and to allow more people to contribute, in this series of posts I will do my best to break the code design, focusing at one part at a time.</p><p>This post is an overview of Surrealdb, on the next posts we will dive into the code itself.</p><h3>What is Surrealdb?</h3><blockquote>According to the documentation:<br><br>SurrealDB is an innovative NewSQL cloud database, suitable for serverless applications, jamstack applications, single-page applications, and traditional applications.</blockquote><p>Let’s break that down.</p><p><strong>NewSQL — </strong>a database that offers a NoSQL experience (with no structured data constraints), whole also maintaining the ACID guarantees (transactional execution, data consistency and rollback capability).</p><p><strong>Serverless Cloud — </strong>surrealdb can be deployed on a distributed architecture and can run on all major providers, enabling the database to scale as needed.</p><p><strong>Application Backend — </strong>surrealdb has additional features that allow it to be used directly from a customer facing client as well as from a custom server. These features include:</p><ul><li>An advanced premissions model</li><li>Live updates via Websockets</li><li>A REST API</li><li>And more…</li></ul><h3>SurrealDB data model</h3><p>Sound awesome! What can you store in this DB?</p><p>Basically — Anything.</p><p>SurrealDB supports three types of data paradigms, all working together to create a modern data model:</p><ol><li>Unstructured data — Arbitrary objects with no defined structure, including nested references to other objects in the database</li><li>Tabular (structured) data — like classical SQL databases, storing rows with a defined and enforced structure</li><li>Graph connections — arbitrary connections between objects using a graph syntax, which allows the connection itself to have properties that can be queried</li></ol><p>This means that we can save whatever data we want, decide if we want it to have a structure, and connect different objects into a graph that we can query.</p><p>All data is stored in groups that for simplicity’s sake are called <em>“tables”</em>, even when the data is unstructured.</p><h3>Example Time</h3><p>All operations on the database are pretty much intuitive for anyone who has dealt with any kind of database before.</p><p>(The example is based on the <a href="https://github.com/surrealdb/surrealdb#quick-look">official documentation</a>):</p><p>Working with unstructured data:</p><pre>UPDATE person SET<br>	waist = &lt;int&gt; &quot;34.59&quot;,<br>	height = &lt;float&gt; 201,<br>	score = &lt;decimal&gt; 0.3 + 0.3 + 0.3 + 0.1<br>;</pre><p>Or with adding structure:</p><pre>-- Create a schemafull table<br>DEFINE TABLE user SCHEMAFULL;<br><br>-- Specify fields on the user table<br>DEFINE FIELD name ON TABLE user TYPE object;<br>DEFINE FIELD name.first ON TABLE user TYPE string;<br>DEFINE FIELD name.last ON TABLE user TYPE string;<br>DEFINE FIELD email ON TABLE user TYPE string ASSERT is::email($value);<br><br>-- Add a unique index on the email field preventing duplicate values<br>DEFINE INDEX email ON TABLE user COLUMNS email UNIQUE;</pre><p>On top of this data we can build our graph:</p><pre>-- Add a graph edge between user:tobie and article:surreal<br>RELATE user:ori-&gt;write-&gt;article:welcome-to-wonderland<br>	SET time.written = time::now()<br>;<br><br>-- Add a graph edge between specific users and developers<br>LET $from = (SELECT users FROM site:medium);<br>LET $devs = (SELECT * FROM user WHERE tags CONTAINS &#39;surrealdb&#39;);<br>RELATE $from-&gt;like-&gt;$devs UNIQUE<br>	SET time.connected = time::now()<br>;</pre><p>And all of this can be done via a REST API, custom client libraries in various languages, or websockets (see the <a href="https://surrealdb.com/docs/integration">docs</a>).</p><h3>Conclusion</h3><p>I’ve only touched the surface of the functionality of this amazing tool, it can do so much more.</p><p>I encourage you to read <a href="https://surrealdb.com/">the docs</a>, or watch this short Youtube video if you only want a taste:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FDPQbuW9dQ7w%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DDPQbuW9dQ7w&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FDPQbuW9dQ7w%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/3837649f7e65441e312e6d25229349a1/href">https://medium.com/media/3837649f7e65441e312e6d25229349a1/href</a></iframe><p>Future posts will dive into the code base itself (which is open source and can be found <a href="https://github.com/surrealdb/surrealdb">here</a>) and will be focused on the community, with an aim of enabling more people to get their hands dirty and contribute.</p><p>Follow to get notified when the posts go live!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4444242353e6" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>