<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: lisahui</title>
    <description>The latest articles on DEV Community by lisahui (@lisahui).</description>
    <link>https://dev.to/lisahui</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F710953%2Fa0187296-55f1-4744-91ed-b8aa4260a877.JPG</url>
      <title>DEV Community: lisahui</title>
      <link>https://dev.to/lisahui</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lisahui"/>
    <language>en</language>
    <item>
      <title>How to use one Dashboard to see your cluster heath</title>
      <dc:creator>lisahui</dc:creator>
      <pubDate>Tue, 01 Mar 2022 07:23:28 +0000</pubDate>
      <link>https://dev.to/lisahui/how-to-use-one-dashboard-to-see-your-cluster-heath-p7h</link>
      <guid>https://dev.to/lisahui/how-to-use-one-dashboard-to-see-your-cluster-heath-p7h</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eGWajYhT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155935795-eaf86ef4-be5e-4e95-a81d-93298ab2526c.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eGWajYhT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155935795-eaf86ef4-be5e-4e95-a81d-93298ab2526c.jpg" alt="Introducing Nebula Dashboard: The brain of your Nebula Graph cluster" width="880" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Distributed database systems like Nebula Graph perform well in storing data but they make DevOps difficult and complicated. Building and managing clusters is pain and time-consuming. Not to mention it is not easy to back up and upgrade the system, especially in the production environment.&lt;/p&gt;

&lt;p&gt;Introducing &lt;a href="https://nebula-graph.io/products/dashboard/"&gt;Nebula Dashboard&lt;/a&gt;, a visualization tool that helps you manage your Nebula Graph clusters in an intuitive web user interface. Nebula Dashboard can help DevOps engineers and database administrators (DBAs) reduce the daily cost of managing a Nebula Graph cluster and ensure the stability of their systems.&lt;/p&gt;

&lt;p&gt;If you want a free trial of Nebula Dashboard enterprise edition, click here: &lt;a href="https://dashboard.nebula-graph.io/"&gt;https://dashboard.nebula-graph.io/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The structure
&lt;/h2&gt;

&lt;p&gt;The following figure gives the six main features of Nebula Dashboard. They are cluster management, monitoring, alarming, single cluster configuration, access control, and system setting. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HY39jW1G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155936570-00156e85-fc82-4792-b638-d5cc7fc98287.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HY39jW1G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155936570-00156e85-fc82-4792-b638-d5cc7fc98287.png" alt="Nebula Dashboard Structure" width="880" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The one dashboard to rule them all
&lt;/h2&gt;

&lt;p&gt;First, let's dive into the main features of Nebula Dashboard: multi-cluster orchestration &amp;amp; lifecycle management, monitoring and alerting, and access control.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-cluster orchestration &amp;amp; lifecycle management
&lt;/h3&gt;

&lt;p&gt;The lifecycle for a Nebula Graph cluster refers to the journey from the creation of a cluster to management to eventual recycling.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7Cilv2uK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155936823-5cf11b30-f9d3-49d4-a793-f84e14e7451b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7Cilv2uK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155936823-5cf11b30-f9d3-49d4-a793-f84e14e7451b.png" alt="Nebula Dashboard rules all" width="880" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nebula Dashboard can manage the full lifecycle of your Nebula Graph cluster in a visualized way. &lt;/p&gt;

&lt;p&gt;When you have not created a Nebula Graph cluster, Nebula Dashboard enables you to start the creation of a new cluster. If you are already working with Nebula Graph clusters, you can import those clusters into Nebula Dashboard in batch and monitor or manage them there. We will go through the entire process of creating and managing clusters in this chapter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create a cluster from Nebula Dashboard&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Vo8j_468--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155937028-dbd2ac45-7784-4fa4-b3f8-47774cb89579.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Vo8j_468--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155937028-dbd2ac45-7784-4fa4-b3f8-47774cb89579.png" alt="Create a cluster from Nebula Dashboard" width="880" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are five steps to create a cluster, as shown in the figure above:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Input a unique cluster name&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Choose the Nebula Graph version that you want to create a cluster. Please note that Nebula Dashboard only supports Nebula Graph v2.0 and above. By default, Nebula Dashboard provides three built-in installation package: v2.6.1, v2.5.1, and v2.0.1. It will also support Nebula Graph v3.0.0 once it is released in mid February 2022.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add nodes to the cluster. You may need to authorize the step from SSH.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select the services to be deployed on different nodes. For simplicity, you can also use the "Auto Add Service” feature to evenly deploy services to different nodes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Confirm everything is OK and click “Create Cluster.” And voilà, you have your Nebula Graph cluster up and running.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Importing a cluster into Nebula Dashboard&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you have a Nebula Graph cluster running, you can import it into Nebula Dashboard to manage it in a graphic user interface.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rrmFGgoJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155937402-c6c78c8e-6f70-4422-8d6b-dc6616042d27.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rrmFGgoJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155937402-c6c78c8e-6f70-4422-8d6b-dc6616042d27.png" alt="Importing a cluster into Nebula Dashboard" width="880" height="359"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IfWWVWsy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155937479-b1b765e4-ea2a-459a-8053-968f7f2042ed.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IfWWVWsy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155937479-b1b765e4-ea2a-459a-8053-968f7f2042ed.png" alt="Importing a cluster into Nebula Dashboard" width="880" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Cluster operations and maintenance
&lt;/h3&gt;

&lt;p&gt;Once you have completed the above two steps, you can use Nebula Dashboard to manage your clusters. Cluster management can be tedious. It involves repeated actions such as management of clusters, nodes and services. Nebula Dashboard can free DevOps engineers from this unbearable boredom by simplifying the whole process of cluster management. &lt;/p&gt;

&lt;p&gt;Here are what you can do with Nebula Dashboard to manage your cluster:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Node and service management&lt;/strong&gt;&lt;br&gt;
Nebula Dashboard’s Node management and Service management modules will give you an intuitive overview of all the information about running nodes in the cluster and services in each node.  You can do actions such as adding empty nodes and starting or stopping services in a node.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OE-IDmpV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155937599-0fdca548-7df2-46ed-9c87-c8b3be473a3a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OE-IDmpV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155937599-0fdca548-7df2-46ed-9c87-c8b3be473a3a.png" alt="Node and service management" width="880" height="348"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Hxgt3MAq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155938301-68a54552-1dc0-4160-aa03-9756d63c3994.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Hxgt3MAq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155938301-68a54552-1dc0-4160-aa03-9756d63c3994.png" alt="Node and service management" width="880" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Be informed. Be alerted.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Monitoring the health of a cluster is the first priority forNebula Dashboard. Nebula Dashboard provides cluster overview, node metrics monitoring, service metrics monitoring, and alarm notification capabilities to help DevOps engineers stay informed of their Nebula Graph cluster health.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cluster overview&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the overview section, DevOps engineers can quickly understand the overall situation of the current cluster, including the distribution of services in the node, operational status, and customize indicators of their most concerned metrics to stay alarmed.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e11KfzaZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155938434-91ac15f9-8652-4020-9cc8-15f1005e2057.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e11KfzaZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155938434-91ac15f9-8652-4020-9cc8-15f1005e2057.png" alt="Cluster overview" width="880" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Node monitoring&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Node monitoring is used to present all the information of a particular Nebula Graph node, including its CPU usage, memory, load, and disk. You can also set a baseline for a specific metric so that you can be alerted when the metric exceeds the baseline. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RplF-UjC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155938547-adb5798b-bd23-4f32-b989-d5f044b863d4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RplF-UjC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155938547-adb5798b-bd23-4f32-b989-d5f044b863d4.png" alt="Node monitoring" width="880" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Service monitoring&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Service monitoring mainly involves the metrics of three services of Nebula Graph clusters. The three services are graphd, metad and storaged. Currently, Nebula Dashboard can present dozens of cluster metrics and monitor them based on their aggregations and averages. These metrics include the number of slow queries and errors in the graphd service, vertex  and edge latencies of storaged, and heartbeat latencies of the metad service. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--aKU2SAIU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155938646-d1e9741f-1fc8-43ee-9cc0-5bb0a3b06d65.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aKU2SAIU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155938646-d1e9741f-1fc8-43ee-9cc0-5bb0a3b06d65.png" alt="Service monitoring" width="880" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alert&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nebula Dashboard's alert module is a service for alerting the monitoring indicators of Nebula Graph clusters. You can view alert information, set alert rules and alert recipients in the module. &lt;/p&gt;

&lt;p&gt;You can set up alert baselines of indicators that you care about, and the frequency and duration of the alert triggering, as well as the notification message template, so that the system will automatically send out notifications to alert you when any of the indicators triggers the corresponding rules.&lt;/p&gt;

&lt;p&gt;You can view history alert messages, in-site alert messages, set up email and webhook notifications, and set alert rules in the alert module. Here is how the module works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create custom alert rules or activate alert rules.&lt;/li&gt;
&lt;li&gt;An alert message will be sent when the system monitors any abnormal metric.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A pop-up in-site message of the alert is shown on the top right of Nebula Dashboard. The system will send out an alert email if you have set an email recipient.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can then perform troubleshooting based on the alert message.  &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ceDzpi1h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155938848-384522e4-3b99-4d50-83ed-7d633143d118.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ceDzpi1h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/155938848-384522e4-3b99-4d50-83ed-7d633143d118.png" alt="Alert" width="880" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fine-grained access control&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As a multi-cluster management tool, Nebula Dashboard has enabled a fine-grained access control system to ensure security.  Nebula Dashboard has designed two roles, and they are admins and users. While admins are authorized to perform overall configuration of the Dashboard, users are only entitled to operate within the scope that is assigned to them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LDAP login&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nebula Dashboard supports login with Lightweight Directory Access Protocol (LDAP). Once the LDAP information is provided in the deployment phase, users can log in with their enterprise account. Admins can invite users by sending them an email containing a verification link.&lt;/p&gt;

&lt;h3&gt;
  
  
  Feature roadmap
&lt;/h3&gt;

&lt;p&gt;Nebula Dashboard is designed to simplify the daily tasks of DevOps engineers and database administrators (DBAs). It will support the following features in the forthcoming versions.&lt;/p&gt;

&lt;p&gt;Back up and upgrade a cluster with one click. This feature will only be compatible with Nebula Graph v3.0 and above.&lt;br&gt;
Visualized management of Storage Zone&lt;br&gt;
TV dashboard that allows you to display key metrics in a customized way in a display in your office &lt;br&gt;
Slow query management that allows DBAs to quickly identify slow queries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wanna try?
&lt;/h2&gt;

&lt;p&gt;Check out the Nebula Dashboard playground to see how Nebula Dashboard works in practice..  You can also click this link to request a 15-day free trial to test Nebula Dashboard enterprise edition out in your own environment.&lt;/p&gt;

&lt;p&gt;If you encounter any problems in the process of using Nebula Graph, please refer to &lt;a href="https://docs.nebula-graph.io/3.0.0/pdf/NebulaGraph-EN.pdf"&gt;Nebula Graph Database Manual&lt;/a&gt; to troubleshoot the problem. It records in detail the knowledge points and specific usage of the graph database and the graph database Nebula Graph.&lt;/p&gt;

&lt;p&gt;Join our &lt;a href="https://join.slack.com/t/nebulagraph/shared_invite/zt-7ybejuqa-NCZBroh~PCh66d9kOQj45g"&gt;Slack channel&lt;/a&gt; if you want to discuss with the rest of the Nebula Graph community!&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>devops</category>
      <category>database</category>
    </item>
    <item>
      <title>What I learned working on Nebula Graph, an open source and distributed graph database</title>
      <dc:creator>lisahui</dc:creator>
      <pubDate>Thu, 24 Feb 2022 10:27:05 +0000</pubDate>
      <link>https://dev.to/lisahui/what-i-learned-working-on-nebula-graph-an-open-source-and-distributed-graph-database-16eo</link>
      <guid>https://dev.to/lisahui/what-i-learned-working-on-nebula-graph-an-open-source-and-distributed-graph-database-16eo</guid>
      <description>&lt;p&gt;This article is based on a talk given by Dr. Min Wu, a senior expert at vesoft. Dr. Wu talked about the status quo of the global graph database market, the design and features of Nebula Graph as a distributed graph database, as well as Nebula Graph’s open-source community.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Global Graph Database Market
&lt;/h2&gt;

&lt;p&gt;Let’s start with some numbers. Markets and Markets anticipates the graph database market will reach $2.4 billion by 2023 from $821.8 million in 2018.&lt;/p&gt;

&lt;p&gt;Graph database is still in a rising trend and it is one of Gartner’s top 10 data and analytics trends in 2021. This is because graph databases can be used in many more areas than traditional databases, including computing, processing, deep learning, and machine models.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153827006-decd54de-76fa-4123-83e3-739512013fce.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153827006-decd54de-76fa-4123-83e3-739512013fce.png" alt="The Global Graph Database Market"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Graph databases gained the most in popularity in the past 10 years, according to data compiled by DB-Engines. The data is based on social media mentions, Stack Overflow questions, and search trends.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153827206-90b5b420-99b4-466c-904c-78250115ee1e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153827206-90b5b420-99b4-466c-904c-78250115ee1e.png" alt="The Global Graph Database Market"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Advantages of Graph Databases
&lt;/h2&gt;

&lt;p&gt;One of the most significant advantages of graph databases is that they are intuitive. If you want to express the character relationships of Game of Thrones, you can use both traditional tabular databases and graph databases. But as it is shown below, using graph data is much more intuitive, though they both express the same data model.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153827594-7c9f5e95-ae9d-499a-bb5b-f41cb48d3578.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153827594-7c9f5e95-ae9d-499a-bb5b-f41cb48d3578.png" alt="Advantages of Graph Databases"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(Tabular data)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153983950-58cd84d4-8bed-400e-8daa-ce1e04549172.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153983950-58cd84d4-8bed-400e-8daa-ce1e04549172.png" alt="Advantages of Graph Databases"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(Graph data)&lt;/p&gt;

&lt;p&gt;In another example, we can compare how to search in SQL databases and graph databases. For example, here is how to find out how many posts and comments were created in a given time frame and rank the results in SQL databases and graph databases.&lt;/p&gt;

&lt;p&gt;SQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--PostgreSQL 

WITH RECURSIVE post_all (psa_threadid

                      , psa_thread_creatorid, psa_messageid

                      , psa_creationdate, psa_messagetype

                       ) AS (

    SELECT m_messageid AS psa_threadid

         , m_creatorid AS psa_thread_creatorid

         , m_messageid AS psa_messageid

         , m_creationdate, 'Post'

      FROM message

     WHERE 1=1 AND m_c_replyof IS NULL -- post, not comment

       AND m_creationdate BETWEEN :startDate AND :endDate

  UNION ALL

    SELECT psa.psa_threadid AS psa_threadid

         , psa.psa_thread_creatorid AS psa_thread_creatorid

         , m_messageid, m_creationdate, 'Comment'

      FROM message p, post_all psa

     WHERE 1=1 AND p.m_c_replyof = psa.psa_messageid

     AND m_creationdate BETWEEN :startDate AND :endDate

)

SELECT p.p_personid AS "person.id"

     , p.p_firstname AS "person.firstName"

     , p.p_lastname AS "person.lastName"

     , count(DISTINCT psa.psa_threadid) AS threadCount

END) AS messageCount

     , count(DISTINCT psa.psa_messageid) AS messageCount

  FROM person p left join post_all psa on (

       1=1   AND p.p_personid = psa.psa_thread_creatorid

   AND psa_creationdate BETWEEN :startDate AND :endDate

   )

 GROUP BY p.p_personid, p.p_firstname, p.p_lastname

 ORDER BY messageCount DESC, p.p_personid

 LIMIT 100;
Here is how to realize the same query using the Cypher graph query language:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--Cypher
MATCH (person:Person)&amp;lt;-[:HAS_CREATOR]-(post:Post)&amp;lt;-[:REPLY_OF*0..]-(reply:Message)
WHERE  post.creationDate &amp;gt;= $startDate   AND  post.creationDate &amp;lt;= $endDate
  AND reply.creationDate &amp;gt;= $startDate   AND reply.creationDate &amp;lt;= $endDate
person. RETURN
id,   person.firstName,   person.lastName,   count(DISTINCT post) AS threadCount,
  count(DISTINCT reply) AS messageCount
ORDER BY
  messageCount DESC,  person.id ASC
LIMIT 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In addition, the graph ecosystem is diversified. The following is the graph technology landscape in 2020, and we can expect more graph related technologies coming along in 2021.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153828477-22d4c36c-63ea-438e-9ee6-ed3c618cd37a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153828477-22d4c36c-63ea-438e-9ee6-ed3c618cd37a.png" alt="Advantages of Graph Databases"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Nebula Graph
&lt;/h2&gt;

&lt;p&gt;Now, let’s take Nebula Graph, a distributed graph database, as an example to talk about the evolution of graph technology. I will also share the challenges the team had faced when developing Nebula Graph and how we had solved them.&lt;/p&gt;

&lt;p&gt;When we started designing the blueprint of Nebula Graph in late 2018, the team had set four goals for the database. They are scalability, production-ready, OLTP(Online Transaction Processing), and open source. The four goals are still influencing the roadmapping of Nebula Graph until today.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scalability&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Scalability is the No.1 design principle of Nebula Graph. This is because we believe the data that businesses will process in the future must be massive and there will be no way that single machines can handle them. That’s why we designed Nebula Graph in a way that it is capable of handling graph data with trillions of vertices and edges.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Production-ready&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nebula Graph is also designed to be production-ready on the first day, including the design of its query language, visualization, programmability, and DevOps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OLTP&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;OLTP (online transactional processing) enables the real-time execution of large numbers of database transactions by large numbers of users, typically over the internet. One of the priorities of Nebula Graph’s design is OLTP. This makes Nebula Graph an online, high-concurrency, and low-latency graph database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Open source&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nebula Graph is also devoted to building an open-source community and integrating with the big data world, supporting graph computing and training frameworks like Tencent Plato and Spark GraphX.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153829025-447f8153-0154-443e-96b6-aaf54f620877.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153829025-447f8153-0154-443e-96b6-aaf54f620877.png" alt="Nebula Graph"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Nebula Core
&lt;/h2&gt;

&lt;p&gt;The above figure shows the ecosystem built around Nebula Graph. The section with red background is the Nebula Graph Core, which consists of three parts called Meta, Graph, and Storage.&lt;/p&gt;

&lt;p&gt;Nebula Graph’s query language is our in-house nGQL, which is also compatible with openCypher. We have also developed clients in languages including Java, C++, Python, and Go. Then on the top we have a number of SDKs that can work with frameworks like Spark, Flink, GraphX, Tencent Plato.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153829317-ad4a7330-8b3b-461c-b611-fe017856336c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153829317-ad4a7330-8b3b-461c-b611-fe017856336c.png" alt="The Nebula Core"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s dive into the Nebula core, which, as I mentioned above, consists of the meta, graph, and storage services. The meta service deals with metadata, while the storage service stores the data, and the graph service is in charge of querying. The three modules run on their independent processes, ensuring the separation of compute and storage.&lt;/p&gt;

&lt;p&gt;The meta service manages the schema. Nebula Graph is not a schema-free database, and it requires the properties of vertices and edges to be pre-configured. The meta service also manages storage spaces, long-duration tasks, and data cleaning.&lt;/p&gt;

&lt;p&gt;Nebula Graph can handle data with trillions of vertices and edges. This means the system must segment the data in storage and handling. Nebula Graph uses the segmentation of edges and stores vertices in partitions. Each partition may have a few replicas and run on different machines. The query engine is stateless, meaning that all query data should either be retrieved from the meta service or the storage service and there is no communication between query services.&lt;/p&gt;

&lt;p&gt;The above is about Nebula Graph’s separation of compute and storage. Now let’s talk about data characteristics. We have mentioned that Nebula Graph is not a schema-free database and that all the data stored is pre-defined by Data Definition Languages (DDLs). We call types of vertices Tag and types of edges EdgeType. Vertices are defined using a 2-tuple consisting of the vid and the tag. Edges are defined using a 4-tuple consisting of the endpoints, EdgeType, and rank.&lt;/p&gt;

&lt;p&gt;Nebula Graph supports primitive data types like boolean, int, and double as well as composite types like list, set, or graph data types like path and subgraph. If there is long string data stored in the database, it is usually indexed by Elasticsearch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153829515-9259bd0c-d127-43ee-9724-0d2561619296.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153829515-9259bd0c-d127-43ee-9724-0d2561619296.png" alt="The Nebula Core"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is some additional information about the storage engine. For the query engine graphd, the external interface exposed by the storage engine is a distributed graph service, but it can also be used as a distributed key-value (KV) service if necessary. In the storage engine, partitions adopt the Raft consensus protocol. Nebula Graph stores vertices and edges in separated partitions. The following figure is about how KV is implemented using the storage partitioning.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153829730-a73d0aa3-ab99-404e-b355-06e1b98111d6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153829730-a73d0aa3-ab99-404e-b355-06e1b98111d6.png" alt="The Nebula Core"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Nebula Graph, each edge is stored as two pieces of data. As mentioned above, the storage layer relies on VID and guarantees strong consistency using the Raft protocol.&lt;/p&gt;

&lt;p&gt;Nebula Graph uses ElasticSearch for full-text index. From Nebula Graph v2.x, our R&amp;amp;D team has optimized the write performance of Nebula’s indexing capability. Since v2.5.0, Nebula Graph has started to support the combination of data expiration TTL and indexing. And from v2.6.0, Nebula Graph started to support the TOSS (Transaction on storage side) function to achieve the eventual consistency of edges. That is to say, edges are either successfully written or failed at the same time when they are inserted or modified.&lt;/p&gt;

&lt;p&gt;Nebula Graph uses its in-house nGQL as its query language. In June 2021, the International Organization for Standardization has drafted the standard for the syntax and semantics of GQL and there is a consensus between major graph database vendors.&lt;/p&gt;

&lt;p&gt;From Nebula Graph v2.0, nGQL started to be compatible with openCypher, which was an open-source version of Neo4j’s Cypher query language. Now, nGQL supports the Doctrine Query Language (DQL) of openCypher and has developed its own vanilla nGQL syntax style in Data Manipulation Language (DML) and Data Definition Language (DDL).&lt;/p&gt;

&lt;p&gt;We also mentioned that Nebula Graph is born to be production-ready. So it supports a wide range of operation features like data isolation, user permission and authentication, and replica configuration. Also, Nebula Graph supports clustering. Nebula Operator, which was released in April 2021, started to support Kubernetes.&lt;/p&gt;

&lt;p&gt;As for the performance of Nebula Graph, most performance tests are carried out by users in the community, such as engineers from tech companies like Meituan, WeChat, 360 DigiTech, and WeBank, etc. The figures below are performance reports compiled by users.&lt;/p&gt;

&lt;p&gt;As for the performance of Nebula Graph, most performance tests are carried out by users in the community, such as Meituan, WeChat, 360 DigiTech, WeBank. The figures below are performance reports compiled by users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153830033-15cfdf2f-fa39-457f-96b4-b83bebdab759.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153830033-15cfdf2f-fa39-457f-96b4-b83bebdab759.png" alt="The Nebula Core"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153830090-2b9d1624-00ea-42d0-a6b4-737719683b41.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153830090-2b9d1624-00ea-42d0-a6b4-737719683b41.png" alt="The Nebula Core"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have mentioned that one of the priorities of Nebula Graph’s design is OLTP. But that doesn’t mean it neglects analytical processing (AP). Nebula Graph has integrated AP frameworks like Apache Spark’s GraphX and Plato developed by Tencent.&lt;/p&gt;

&lt;p&gt;Generally speaking, Nebula Graph performance has improved significantly when the deep traversal is conducted.&lt;/p&gt;

&lt;p&gt;Nebula Graph meets users’ AP (Access Point) requirements of OLTP (Online Transactional Processing ) by docking with Spark’s Graph X and supports Plato, the graph computing engine of Tencent’s WeChat team. Plato docking is actually the data connection between the two engines, which needs to change the internal data format of Nebula Graph to be that of Plato and then Partition to map them one by one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Nebula Graph Community
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153830303-56568605-7547-4c00-a3ff-9d6e7b78e398.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153830303-56568605-7547-4c00-a3ff-9d6e7b78e398.png" alt="The Nebula Graph Community"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nebula Graph became open source in May 2019. Its v1.0 GA was released in June 2020, even though some companies had applied Nebula Graph in production before that. Nebula Graph first entered DB-Engines’ graph database management system ranking two years ago and now it ranks 15th on the list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153830477-11d7f607-aecb-468e-b7f5-f0ab360ff71b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153830477-11d7f607-aecb-468e-b7f5-f0ab360ff71b.png" alt="DTCC2021"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;NOTE: The screenshot is the DB-Engine ranking in Apr. 2021 when this talk was given.&lt;/p&gt;

&lt;p&gt;Nebula Graph is also one of the top open source players in China. This following report, which was published by the X-Lab of East China Normal University, ranks the companies according to the community popularity of their open source products. Vesoft Inc., the maker of Nebula Graph, ranks the eighth, before TikTok parent Bytedance, and just one position after Huawei.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153830897-9cc1d126-1735-48c4-a8c3-10db68eee805.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F153830897-9cc1d126-1735-48c4-a8c3-10db68eee805.png" alt="The Nebula Graph Community"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are some of my thoughts about open source graph databases. Open source is very common in the graph database industry because it is a relatively new area and only got traction in recent years. This is why Nebula Graph chose to be open source from day one. Open source software can also attract more developers to use and gain valuable feedback from adopters.&lt;/p&gt;

&lt;p&gt;If you encounter any problems in the process of using Nebula Graph, please refer to Nebula Graph Database Manual to troubleshoot the problem. It records in detail the knowledge points and specific usage of the graph database and the graph database Nebula Graph.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>database</category>
      <category>deveoper</category>
    </item>
    <item>
      <title>Nebula Graph v3.0.0 Release Note</title>
      <dc:creator>lisahui</dc:creator>
      <pubDate>Thu, 24 Feb 2022 09:58:52 +0000</pubDate>
      <link>https://dev.to/lisahui/nebula-graph-v300-release-note-525g</link>
      <guid>https://dev.to/lisahui/nebula-graph-v300-release-note-525g</guid>
      <description>&lt;p&gt;&lt;strong&gt;Nebula Graph v3.0.0&lt;/strong&gt; is here! The new version introduced a series of new features including enhanced backup and restore, better support for the openCypher query language, and more fine-grained user management.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Support backup and restore. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3469"&gt;https://github.com/vesoft-inc/nebula/pull/3469&lt;/a&gt; 
&lt;a href="https://github.com/vesoft-inc/nebula-agent/pull/1"&gt;https://github.com/vesoft-inc/nebula-agent/pull/1&lt;/a&gt; 
&lt;a href="https://github.com/vesoft-inc/nebula-br/pull/22"&gt;https://github.com/vesoft-inc/nebula-br/pull/22&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Support openCypher queries with multiple MATCH statements. &lt;a href="https://github.com/vesoft-inc/nebula/pull/3519"&gt;https://github.com/vesoft-inc/nebula/pull/3519&lt;/a&gt; &lt;a href="https://github.com/vesoft-inc/nebula/pull/3318"&gt;https://github.com/vesoft-inc/nebula/pull/3318&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Support Standalone Nebula Graph. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3310"&gt;https://github.com/vesoft-inc/nebula/pull/3310&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Support key value separation for the storage engine. &lt;a href="https://github.com/vesoft-inc/nebula/pull/3281"&gt;https://github.com/vesoft-inc/nebula/pull/3281&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Support topN push down for LOOKUP. &lt;a href="https://github.com/vesoft-inc/nebula/pull/3499"&gt;https://github.com/vesoft-inc/nebula/pull/3499&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Support vertex without tag. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3316"&gt;https://github.com/vesoft-inc/nebula/pull/3316&lt;/a&gt; &lt;a href="https://github.com/vesoft-inc/nebula/pull/3335"&gt;https://github.com/vesoft-inc/nebula/pull/3335&lt;/a&gt; &lt;a href="https://github.com/vesoft-inc/nebula/pull/3328"&gt;https://github.com/vesoft-inc/nebula/pull/3328&lt;/a&gt; &lt;a href="https://github.com/vesoft-inc/nebula/pull/3286"&gt;https://github.com/vesoft-inc/nebula/pull/3286&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Support parameterized queries. &lt;a href="https://github.com/vesoft-inc/nebula/pull/3379"&gt;https://github.com/vesoft-inc/nebula/pull/3379&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Support queries without specifying VIDs but a LIMIT clause must be used to restrict the number of results. &lt;a href="https://github.com/vesoft-inc/nebula/pull/3320"&gt;https://github.com/vesoft-inc/nebula/pull/3320&lt;/a&gt; &lt;a href="https://github.com/vesoft-inc/nebula/pull/3329"&gt;https://github.com/vesoft-inc/nebula/pull/3329&lt;/a&gt; &lt;a href="https://github.com/vesoft-inc/nebula/pull/3262"&gt;https://github.com/vesoft-inc/nebula/pull/3262&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Support duration. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3338"&gt;https://github.com/vesoft-inc/nebula/pull/3338&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Support most UTF-8 encoded characters of 1 to 4 bytes in Schema. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3380"&gt;https://github.com/vesoft-inc/nebula/pull/3380&lt;/a&gt; &lt;a href="https://github.com/vesoft-inc/nebula/pull/3440"&gt;https://github.com/vesoft-inc/nebula/pull/3440&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Support DESCRIBE USER. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3300"&gt;https://github.com/vesoft-inc/nebula/pull/3300&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Optimizations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Refactor cluster management. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3343"&gt;https://github.com/vesoft-inc/nebula/pull/3343&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add log monitor to check free bytes of log disks, change log level when space is almost full. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3576"&gt;https://github.com/vesoft-inc/nebula/pull/3576&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Support any string for tag names in apostrophe. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3424"&gt;https://github.com/vesoft-inc/nebula/pull/3424&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Support that the storage service sends partition disk paths to the meta. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3369"&gt;https://github.com/vesoft-inc/nebula/pull/3369&lt;/a&gt; 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3416"&gt;https://github.com/vesoft-inc/nebula/pull/3416&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add constraints on invalid password attempts. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3573"&gt;https://github.com/vesoft-inc/nebula/pull/3573&lt;/a&gt; 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3629"&gt;https://github.com/vesoft-inc/nebula/pull/3629&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Support DELETE in TOSS. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3374"&gt;https://github.com/vesoft-inc/nebula/pull/3374&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Support to use logrotate. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3541"&gt;https://github.com/vesoft-inc/nebula/pull/3541&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Support more metrics. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3446"&gt;https://github.com/vesoft-inc/nebula/pull/3446&lt;/a&gt; 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3605"&gt;https://github.com/vesoft-inc/nebula/pull/3605&lt;/a&gt; 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3590"&gt;https://github.com/vesoft-inc/nebula/pull/3590&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Enhancement datetime parser. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3179"&gt;https://github.com/vesoft-inc/nebula/pull/3179&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Remove read lock in meta service to reduce the side effect of read-write locks. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3256"&gt;https://github.com/vesoft-inc/nebula/pull/3256&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Refactor storage indexes to solve the coupling problem between services. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3196"&gt;https://github.com/vesoft-inc/nebula/pull/3196&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Support specifying the floating point accuracy of the round() function. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3178"&gt;https://github.com/vesoft-inc/nebula/pull/3178&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Support https for ES client. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3150"&gt;https://github.com/vesoft-inc/nebula/pull/3150&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Move version info outside of heartbeat.
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3378"&gt;https://github.com/vesoft-inc/nebula/pull/3378&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Support empty list, set, map. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3302"&gt;https://github.com/vesoft-inc/nebula/pull/3302&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Support specifying s2 region coverage parameters when creating a geo index. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3396"&gt;https://github.com/vesoft-inc/nebula/pull/3396&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add version info for SHOW HOSTS. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3702"&gt;https://github.com/vesoft-inc/nebula/pull/3702&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Bugfix
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Fix the bug that memory isn’t released when a default value is used ​​and no value is specified in nGQL. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3666"&gt;https://github.com/vesoft-inc/nebula/pull/3666&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix the bug that the function coalesce() cannot be used. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3653"&gt;https://github.com/vesoft-inc/nebula/pull/3653&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix the bug that using multiple INSERT on an indexed tag will lead to incorrect LOOKUP results. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3627"&gt;https://github.com/vesoft-inc/nebula/pull/3627&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix the crash when the expression exceeds the depth. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3606"&gt;https://github.com/vesoft-inc/nebula/pull/3606&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Disable the aggregate function in YIELD clause and WHERE clauses of nGQL. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3597"&gt;https://github.com/vesoft-inc/nebula/pull/3597&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix the crash when using the aggregate function in UNWILD and WHERE clauses. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3397"&gt;https://github.com/vesoft-inc/nebula/pull/3397&lt;/a&gt; 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3355"&gt;https://github.com/vesoft-inc/nebula/pull/3355&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix the bug that the tag index is rebuilt with an old schema version value. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3332"&gt;https://github.com/vesoft-inc/nebula/pull/3332&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix the bug that the query results will still contain the expired edges if we use GO...REVERSELY. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3536"&gt;https://github.com/vesoft-inc/nebula/pull/3536&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix the memory estimation error info in CentOS 6.0. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3534"&gt;https://github.com/vesoft-inc/nebula/pull/3534&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix the crash when the LOOKUP statement contains a filter that consists of a logical And expression and an IN expression with only one element. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3525"&gt;https://github.com/vesoft-inc/nebula/pull/3525&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix the bug that metad progress is suspended under high load. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3482"&gt;https://github.com/vesoft-inc/nebula/pull/3482&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix the unwinding subgraph crash. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3506"&gt;https://github.com/vesoft-inc/nebula/pull/3506&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix the DROP SPACE crash when rebuilding an index. &lt;a href="https://github.com/vesoft-inc/nebula/pull/3406"&gt;https://github.com/vesoft-inc/nebula/pull/3406&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix the bug of reading memory stats under cgroup v2. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3419"&gt;https://github.com/vesoft-inc/nebula/pull/3419&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix the bug that DROP TAG INDEX deletes the edge index with same name unexpectedly, and vice versa for the deletion of the tag index. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3413"&gt;https://github.com/vesoft-inc/nebula/pull/3413&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix the bug that edges are not shown after a graph space is cloned. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3351"&gt;https://github.com/vesoft-inc/nebula/pull/3351&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix the index existence check problem. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3315"&gt;https://github.com/vesoft-inc/nebula/pull/3315&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix a bug that running the ALTER statement to query the type property may lead to a null pointer obtained by the graph service. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3325"&gt;https://github.com/vesoft-inc/nebula/pull/3325&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Optimize the raft to make the system more stable. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3172"&gt;https://github.com/vesoft-inc/nebula/pull/3172&lt;/a&gt; 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3435"&gt;https://github.com/vesoft-inc/nebula/pull/3435&lt;/a&gt; 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3358"&gt;https://github.com/vesoft-inc/nebula/pull/3358&lt;/a&gt; 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3322"&gt;https://github.com/vesoft-inc/nebula/pull/3322&lt;/a&gt; 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3031"&gt;https://github.com/vesoft-inc/nebula/pull/3031&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Cancel memory check when the ratio is greater than 1.0. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3289"&gt;https://github.com/vesoft-inc/nebula/pull/3289&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix building with ninja error. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3195"&gt;https://github.com/vesoft-inc/nebula/pull/3195&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix the bug that creating tag and edge with same name at the same time may be both succeed. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3735"&gt;https://github.com/vesoft-inc/nebula/pull/3735&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix the bug that failed to create full-text index for the same tag or edge internal id in different SPACE. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3747"&gt;https://github.com/vesoft-inc/nebula/pull/3747&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix variable inconsistency in YIELD clause and GO statement. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3430"&gt;https://github.com/vesoft-inc/nebula/pull/3430&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Fix the crash when schema version is greater than 256. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3893"&gt;https://github.com/vesoft-inc/nebula/pull/3893&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Incompatibility
&lt;/h2&gt;

&lt;p&gt;Nebula Graph 3.0.0 does not support most ecosystem tools of v2.x, please upgrade the ecosystem tools.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The storage services added in the configuration files cannot be read or written directly. The configuration files only register the storage services into the meta services. You must run the ADD HOSTS command to read and write data on storage servers. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3343"&gt;https://github.com/vesoft-inc/nebula/pull/3343&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Disable ZONE and GROUP. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3776"&gt;https://github.com/vesoft-inc/nebula/pull/3776&lt;/a&gt; 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3825"&gt;https://github.com/vesoft-inc/nebula/pull/3825&lt;/a&gt;
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3330"&gt;https://github.com/vesoft-inc/nebula/pull/3330&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Disable BALANCE DATA. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3756"&gt;https://github.com/vesoft-inc/nebula/pull/3756&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Modify the default session timeout from 0 to 28800 seconds, limit the value between 1 and 604800 seconds. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3357"&gt;https://github.com/vesoft-inc/nebula/pull/3357&lt;/a&gt; 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3807"&gt;https://github.com/vesoft-inc/nebula/pull/3807&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add SHOW LOCAL SESSIONS and SHOW LOCAL QUERIES commands, and deprecate SHOW ALL QUERIES. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3488"&gt;https://github.com/vesoft-inc/nebula/pull/3488&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A tag is not required for a vertex. DELETE VERTEX only deletes the vertices, and does not delete the related outgoing and incoming edges of the vertices. At this time, there will be dangling edges by default. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3316"&gt;https://github.com/vesoft-inc/nebula/pull/3316&lt;/a&gt; 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3335"&gt;https://github.com/vesoft-inc/nebula/pull/3335&lt;/a&gt; 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3328"&gt;https://github.com/vesoft-inc/nebula/pull/3328&lt;/a&gt; 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3286"&gt;https://github.com/vesoft-inc/nebula/pull/3286&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Disable the YIELD clause to return custom variables. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3271"&gt;https://github.com/vesoft-inc/nebula/pull/3271&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The YIELD clause is required in the FETCH, FIND PATH, LOOKUP, GET SUBGRAPH and GO statements. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3056"&gt;https://github.com/vesoft-inc/nebula/pull/3056&lt;/a&gt; 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3139"&gt;https://github.com/vesoft-inc/nebula/pull/3139&lt;/a&gt; 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/2957"&gt;https://github.com/vesoft-inc/nebula/pull/2957&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add non-reserved keywords: s2_max_level, s2_max_cells. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3396"&gt;https://github.com/vesoft-inc/nebula/pull/3396&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;It is required to specify a tag to query properties of a vertex in a MATCH statement. For example, from return v.name to return v.player.name. 
&lt;a href="https://github.com/vesoft-inc/nebula/pull/3255"&gt;https://github.com/vesoft-inc/nebula/pull/3255&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you encounter any problems in the process of using Nebula Graph, please refer to &lt;a href="https://docs.nebula-graph.io/3.0.0/pdf/NebulaGraph-EN.pdf"&gt;Nebula Graph Database Manual&lt;/a&gt; to troubleshoot the problem. It records in detail the knowledge points and specific usage of the graph database and the graph database Nebula Graph.&lt;/p&gt;

&lt;p&gt;Join our &lt;a href="https://join.slack.com/t/nebulagraph/shared_invite/zt-7ybejuqa-NCZBroh~PCh66d9kOQj45g"&gt;Slack channel&lt;/a&gt; if you want to discuss with the rest of the Nebula Graph community!&lt;/p&gt;

</description>
      <category>database</category>
      <category>opensource</category>
      <category>graphql</category>
    </item>
    <item>
      <title>A method to compute the Betweenness Centrality against Nebula Graph</title>
      <dc:creator>lisahui</dc:creator>
      <pubDate>Mon, 20 Dec 2021 03:48:36 +0000</pubDate>
      <link>https://dev.to/lisahui/how-to-compute-the-betweenness-centrality-against-nebula-graph-3ke2</link>
      <guid>https://dev.to/lisahui/how-to-compute-the-betweenness-centrality-against-nebula-graph-3ke2</guid>
      <description>&lt;p&gt;​Betweenness Centrality (BC for short) reflects the significance of vertices in the entire network. This article will introduce how to compute Betweenness Centrality against Nebula Graph.    &lt;/p&gt;

&lt;h2&gt;
  
  
  Relevant Concepts
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Centrality&lt;/strong&gt; represents how central a vertex is in the entire network graph, including Degree Centrality, Closeness Centrality, and Betweenness Centrality, etc. Degree Centrality reflects the popularity of a vertex by counting the number of its incoming and outgoing edges, while Closeness Centrality computes the sum of the length of the shortest paths between a vertex and all other vertices in the graph. Thus, the more central a vertex is, the closer it is to all other vertices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Betweenness Centrality&lt;/strong&gt; counts the number of times a vertex appears on the shortest path between any two other vertices, so as to represent the significance of this vertex to the network connectivity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Betweenness Centrality&lt;/strong&gt; of a vertex is the proportion of the number of paths passing through this vertex in all the shortest paths to the total number of shortest paths.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Computing the Betweenness Centrality of a vertex in a graph can be conducted in a weighted graph or in an unweighted graph.&lt;/strong&gt; For unweighted graphs, Breadth-First Search (BFS for short) is used to find the shortest path, while for weighted graphs, Dijkstra’s algorithm is used.&lt;/p&gt;

&lt;p&gt;The following algorithms are all targeted at undirected graphs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Applicable Scenarios
&lt;/h2&gt;

&lt;p&gt;Betweenness Centrality reflects the significance of vertices in the entire network by measuring how a vertex bridges all other vertices in a graph or network. As we can see, Vertex C in the following figure acts as an important bridging vertex.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ot1d74lzm97f9a19hmc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ot1d74lzm97f9a19hmc.jpg" alt="Applicable Scenarios"&gt;&lt;/a&gt;&lt;br&gt;
Betweenness Centrality can be used to identify&lt;/p&gt;

&lt;p&gt;a. The intermediary entities in anti-fraud scenarios in the field of financial risk control.&lt;/p&gt;

&lt;p&gt;b. Specific disease control genes in the medical field to improve drug targets.&lt;/p&gt;

&lt;p&gt;Betweenness Centrality Algorithm&lt;br&gt;
The Betweenness Centrality of a vertex can be computed as follows:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;C_B = \sum_{s{\not=} v {\not=} t \in V} \frac{\sigma_{st}(v)}{\sigma_{st}}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;（Formula 1）&lt;/p&gt;

&lt;p&gt;In this formula,&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\sigma_{st}(v)&lt;/code&gt; is the number of shortest paths from Vertex s to Vertex t.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\sigma_{st}&lt;/code&gt; is the number of shortest paths that pass through Vertex v.&lt;/p&gt;

&lt;p&gt;Vertex s and Vertex t are a pair of vertices belonging to the vertex set.&lt;/p&gt;

&lt;p&gt;To make it more convenient, the betweenness of each pair of vertices can be computed as:&lt;br&gt;
&lt;code&gt;\delta_{st}(v) = \frac{\sigma_{st}(v)}{\sigma_{st}}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;（Formula 2）&lt;/p&gt;

&lt;p&gt;So Formula 1 can be replaced by Formula 2, which gives rise to Formula 3 as follows:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;C_B(v) = \sum_{s{\not=} v {\not=} t \in V} \delta_{st}(v)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;（Formula 3）&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution Procedure
&lt;/h2&gt;

&lt;p&gt;To get the Betweenness Centrality of Vertex v, namely to get &lt;code&gt;$$\frac{&lt;/code&gt;, we need to know whether Vertex v lies on the path from Vertex s to Vertex t.&lt;/p&gt;

&lt;p&gt;(1) To know whether Vertex v lies on the shortest path from Vertex s to Vertex t, use the following formula &lt;code&gt;d_G&lt;/code&gt; represents the shortest path from Vertex s to Vertex t):&lt;/p&gt;

&lt;p&gt;If Vertex v lies on the shortest path from Vertex s to Vertex t, then &lt;code&gt;d_G(s,t) = d_G(s,v) + d_G(v,t)&lt;/code&gt;&lt;br&gt;
is satisfied.&lt;/p&gt;

&lt;p&gt;（Formula 4）&lt;/p&gt;

&lt;p&gt;&lt;code&gt;d_G(s,v)&lt;/code&gt; and &lt;code&gt;d_G(v,t)&lt;/code&gt; are mutually independent. According to the rules of combination, the total number of shortest paths from Vertex s to Vertex t is the product of the number of shortest paths from Vertex s to Vertex t and the number of shortest paths from Vertex s to Vertex t.&lt;/p&gt;

&lt;p&gt;Based on the above two situations, Formula 5 can be inferred:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\sigma_{st}(v) = \begin{cases}&lt;br&gt;
\sigma_{sv} \times \sigma_{vt} &amp;amp;\text{if } d(s,v) + d(v,t) = d(s,t) \&lt;br&gt;
0 &amp;amp;\text{if } other&lt;br&gt;
\end{cases}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;（Formula 5）&lt;/p&gt;

&lt;p&gt;（2）According to the above formula, we can get the conclusion that the number of shortest paths from Vertex s to Vertex t that pass through Vertex w is the result of &lt;code&gt;\sigma_{st}(w) = \sigma_{sw} \times \sigma_{wt}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;. In the graph, Vertex v is the preceding vertex of Vertex w. Therefore, the formula to count the number of shortest paths from Vertex s to Vertex t passing through Vertex v to Vertex w is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\sigma_{st}(v,{v,w}) = \sigma_{sv} \times \sigma_{wt}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;（Formula 6）&lt;/p&gt;

&lt;p&gt;Here are two situations, &lt;code&gt;t = w&lt;/code&gt;&lt;br&gt;
and &lt;code&gt;t \not= w&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;a. If &lt;code&gt;t = w&lt;/code&gt;&lt;br&gt;
, then &lt;code&gt;\sigma_{wt}&lt;/code&gt;&lt;br&gt;
will not exist and we can get&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\delta(v,{v,w}) = \frac{\sigma_{sv}}{\sigma_{sw}}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;（Formula 7）&lt;/p&gt;

&lt;p&gt;b. If &lt;code&gt;t \not= w&lt;/code&gt;&lt;br&gt;
, then we can get&lt;br&gt;
&lt;code&gt;\delta(v,{v,w}) = \frac{\sigma_{sw}(v)}{\sigma_{sw}} \times&lt;br&gt;
\frac{\sigma_{st}(w)}{\sigma_{st}}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;（Formula 8）&lt;/p&gt;

&lt;p&gt;(3) So considering the above two situations, we can get&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\delta_s(v) = \sum_{w:v \in P_s(w)}(\frac{\sigma_{sw}(v)}{\sigma_{sw}} + \sum_{t \not= w \in V}\frac{\sigma_{sw}(v)}{\sigma_{sw}} \times \frac{\sigma_{st}(w)}{\sigma_{st}}) \&lt;br&gt;
= \sum_{w:v \in P_s(w)}\frac{\sigma_{sw}(v)}{\sigma_{sw}}(1 + \sum_{t \not= w \in V} \frac{\sigma_{st}(w)}{\sigma_{st}}) \&lt;br&gt;
= \sum_{w:v \in P_s(w)}\frac{\sigma_{sw}(v)}{\sigma_{sw}} (1 + \delta_s(w))&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;（Formula 9）&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;w:v \in P_s(w)&lt;/code&gt;&lt;br&gt;
, Vertex v is the predecessor of Vertex w in the path from Vertex s to Vertex w.&lt;/p&gt;

&lt;p&gt;(4) According to the above formula that gets the result of , the algorithm workflow of Betweenness Centrality in unweighted graphs can be given as follows:&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7pwg481s5v0e0nb3s24b.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7pwg481s5v0e0nb3s24b.jpg" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
For unweighted graphs, follow the above process.&lt;/p&gt;

&lt;p&gt;To compute the Betweenness Centrality in weighted graphs requires Dijkstra’s algorithm, that is, to change the code in the first while loop.&lt;/p&gt;

&lt;p&gt;The Betweenness Centrality against Nebula Graph has achieved the algorithms for both weighted and unweighted graphs. For the code, see &lt;a href="https://github.com/vesoft-inc/nebula-algorithm/blob/master/nebula-algorithm/src/main/scala/com/vesoft/nebula/algorithm/lib/BetweennessCentralityAlgo.scala" rel="noopener noreferrer"&gt;https://github.com/vesoft-inc/nebula-algorithm/blob/master/nebula-algorithm/src/main/scala/com/vesoft/nebula/algorithm/lib/BetweennessCentralityAlgo.scala&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Computation Examples
&lt;/h2&gt;

&lt;p&gt;Firstly, read the graph data in Nebula Graph to specify the edge data for data reading.&lt;/p&gt;

&lt;p&gt;Secondly, make a topological graph based on the edge data of Nebula Graph and perform centrality computation.&lt;/p&gt;

&lt;p&gt;The graph data read in Nebula Graph can be illustrated in the following unweighted graph:&lt;/p&gt;

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

&lt;p&gt;Compute the BC of Vertex 1:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The vertex pair with the shortest path passing through Vertex 1&lt;/th&gt;
&lt;th&gt;The total number of shortest paths between the vertex pair&lt;/th&gt;
&lt;th&gt;The number of the shortest path passing through Vertex 1&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2-4&lt;/td&gt;
&lt;td&gt;3 （2-3-4, 2-5-4, 2-1-4）&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The BC of Vertex 1:&lt;/td&gt;
&lt;td&gt;1/3&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Compute the BC of Vertex 2:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The vertex pair with the shortest path passing through Vertex 2&lt;/th&gt;
&lt;th&gt;The total number of shortest paths between the vertex pair&lt;/th&gt;
&lt;th&gt;The number of the shortest path passing through Vertex 2&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1-3&lt;/td&gt;
&lt;td&gt;2 （1-2-3, 1-4-3）&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3-5&lt;/td&gt;
&lt;td&gt;2（3-2-5, 3-4-5）&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The BC of Vertex 2:&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Compute the BC of Vertex 3:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The vertex pair with the shortest path passing through Vertex 3&lt;/th&gt;
&lt;th&gt;The total number of shortest paths between the vertex pair&lt;/th&gt;
&lt;th&gt;The number of the shortest path passing through Vertex 3&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2-4&lt;/td&gt;
&lt;td&gt;3 （2-3-4, 2-5-4, 2-1-4）&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The BC of Vertex 3:&lt;/td&gt;
&lt;td&gt;1/3&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compute the BC of Vertex 4:&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The vertex pair with the shortest path passing through Vertex 4&lt;/th&gt;
&lt;th&gt;The total number of shortest paths between the vertex pair&lt;/th&gt;
&lt;th&gt;The number of the shortest path passing through Vertex 4&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1-3&lt;/td&gt;
&lt;td&gt;2 （1-4-3, 1-2-3）&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3-5&lt;/td&gt;
&lt;td&gt;2（3-4-5, 3-2-5)&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The BC of Vertex 4:&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Compute the BC of Vertex 5:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The vertex pair with the shortest path passing through Vertex 5&lt;/th&gt;
&lt;th&gt;The total number of shortest paths between the vertex pairs&lt;/th&gt;
&lt;th&gt;The number of the shortest path passing through Vertex 5&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2-4&lt;/td&gt;
&lt;td&gt;3 （2-3-4, 2-5-4, 2-1-4）&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The BC of Vertex 5:&lt;/td&gt;
&lt;td&gt;1/3&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Therefore, the BC of each vertex is: Vertex 1: 1/3 Vertex 2: 1 Vertex 3: 1/3 Vertex 4: 1 Vertex 5: 1/3&lt;/p&gt;

&lt;h2&gt;
  
  
  Result Examples
&lt;/h2&gt;

&lt;p&gt;Data: Read the edge data in the Nebula Graph test, and take srcId, dstId, and rank as the triplet of edges in the topological graph (Source Vertex, Destination Vertex, and Rank).&lt;/p&gt;

&lt;p&gt;(root@nebula) [test]&amp;gt; match (v:node) -[e:relation] -&amp;gt; ()  return e&lt;br&gt;
+------------------------------------+&lt;br&gt;
| e                                  |&lt;br&gt;
+------------------------------------+&lt;br&gt;
| [:relation "3"-&amp;gt;"4" @1 {col: "f"}] |&lt;br&gt;
+------------------------------------+&lt;br&gt;
| [:relation "2"-&amp;gt;"3" @2 {col: "d"}] |&lt;br&gt;
+------------------------------------+&lt;br&gt;
| [:relation "2"-&amp;gt;"5" @4 {col: "e"}] |&lt;br&gt;
+------------------------------------+&lt;br&gt;
| [:relation "4"-&amp;gt;"5" @2 {col: "g"}] |&lt;br&gt;
+------------------------------------+&lt;br&gt;
| [:relation "1"-&amp;gt;"5" @1 {col: "a"}] |&lt;br&gt;
+------------------------------------+&lt;br&gt;
| [:relation "1"-&amp;gt;"2" @3 {col: "b"}] |&lt;br&gt;
+------------------------------------+&lt;br&gt;
| [:relation "1"-&amp;gt;"4" @5 {col: "c"}] |&lt;br&gt;
+------------------------------------+&lt;br&gt;
Read the edge data in Nebula Graph, set the graph as unweighted, and compute the Betweenness Centrality of each vertex. The results are as follows:&lt;/p&gt;

&lt;p&gt;vid: 4 BC: 1.0&lt;br&gt;
vid: 1 BC: 0.3333333333333333&lt;br&gt;
vid: 3 BC: 0.3333333333333333&lt;br&gt;
vid: 5 BC: 0.3333333333333333&lt;br&gt;
vid: 2 BC: 1.0&lt;br&gt;
Read the edge data of Nebula Graph, set the graph as weighted, and compute the Betweenness Centrality of each vertex. The results are as follows:&lt;/p&gt;

&lt;p&gt;vid: 4 BC: 2.0&lt;br&gt;
vid: 1 BC: 0.5&lt;br&gt;
vid: 3 BC: 1.0&lt;br&gt;
vid: 5 BC: 2.0&lt;br&gt;
vid: 2 BC: 0.0&lt;/p&gt;

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

&lt;p&gt;Paper: A Faster Algorithm for Betweenness Centrality&lt;br&gt;
The source code of Python’s NetworkX realizing Betweenness Centrality: &lt;a href="https://github.com/networkx/networkx/blob/master/networkx/algorithms/centrality" rel="noopener noreferrer"&gt;https://github.com/networkx/networkx/blob/master/networkx/algorithms/centrality&lt;/a&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>opensource</category>
      <category>devops</category>
    </item>
    <item>
      <title>Perform a Load Testing against Nebula Graph with K6</title>
      <dc:creator>lisahui</dc:creator>
      <pubDate>Thu, 02 Dec 2021 04:08:19 +0000</pubDate>
      <link>https://dev.to/lisahui/how-to-perform-load-testing-against-nebula-graph-with-k6-36f0</link>
      <guid>https://dev.to/lisahui/how-to-perform-load-testing-against-nebula-graph-with-k6-36f0</guid>
      <description>&lt;h2&gt;
  
  
  Why Load Testing Matters in Nebula Graph?
&lt;/h2&gt;

&lt;p&gt;The load testing for the database needs to be conducted usually so that the impact on the system can be monitored in different scenarios, such as query language rule optimization, storage engine parameter adjustment, etc.&lt;/p&gt;

&lt;p&gt;The operating system in this article is the x86 CentOS 7.8.&lt;/p&gt;

&lt;p&gt;The hosts where nebula is deployed are configured with 4C 16G memory, SSD disk, and 10G network.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools Needed for the Load Testing
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/vesoft-inc/nebula-ansible"&gt;nebula-ansible&lt;/a&gt; deploys Nebula Graph services.&lt;br&gt;
&lt;a href="https://github.com/vesoft-inc/nebula-importer"&gt;nebula-importer&lt;/a&gt; imports data into Nebula Graph clusters.&lt;br&gt;
&lt;a href="https://github.com/vesoft-inc/k6-plugin"&gt;k6-plugin&lt;/a&gt; is a K6 extension that is used to perform load testing against the Nebula Graph cluster. The extension integrates with the nebula-go client to send requests during the testing.&lt;br&gt;
&lt;a href="https://github.com/vesoft-inc/nebula-bench"&gt;nebula-bench&lt;/a&gt; generates the LDBC dataset and then imports it into Nebula Graph.&lt;br&gt;
&lt;a href="https://github.com/ldbc/ldbc_snb_datagen_hadoop"&gt;ldbc_snb_datagen_hadoop&lt;/a&gt; is a LDBC data generator.&lt;/p&gt;
&lt;h2&gt;
  
  
  Load Testing Process Overview
&lt;/h2&gt;

&lt;p&gt;The load testing conducted in this article uses the LDBC dataset generated by &lt;code&gt;ldbc_snb_datagen&lt;/code&gt;. The testing process is as follows.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--npX44Doc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/144202245-c82fc9c0-3e1d-4549-91bb-8b9a5797878d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--npX44Doc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/144202245-c82fc9c0-3e1d-4549-91bb-8b9a5797878d.png" alt="Load Testing Process Overview" width="384" height="781"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To deploy the topology, use one host as the load testing runner, and use three hosts to form a Nebula Graph cluster.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4HX3-LTB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/143535649-16fb5000-f850-47f5-85b3-4ab04b779a85.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4HX3-LTB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/143535649-16fb5000-f850-47f5-85b3-4ab04b779a85.png" alt="Load Testing Process Overview" width="723" height="415"&gt;&lt;/a&gt;&lt;br&gt;
To make monitoring easier, the load testing runner also deploys:&lt;/p&gt;

&lt;p&gt;Prometheus&lt;br&gt;
Influxdb&lt;br&gt;
Grafana&lt;br&gt;
node-exporter&lt;br&gt;
The hosts where Nebula Graph is installed also deploy:&lt;/p&gt;

&lt;p&gt;node-exporter&lt;br&gt;
process-exporter&lt;/p&gt;
&lt;h3&gt;
  
  
  Load Testing Steps
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Use nebula-ansible to deploy Nebula Graph
&lt;/h4&gt;

&lt;p&gt;Set up SSH login without passwords a. Log in 192.168.8.60, 192.168.8.61, 192.168.8.62, and 192.168.8.63 respectively. Create a vesoft user and join in sudoer with NOPASSWD. b. Log in 192.168.8.60 to set up SSH.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh-keygen

ssh-copy-id vesoft@192.168.8.61
ssh-copy-id vesoft@192.168.8.62
ssh-copy-id vesoft@192.168.8.63
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Download nebula-ansible, install Ansible, and modify the Ansible configuration.
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo yum install ansible -y
git clone https://github.com/vesoft-inc/nebula-ansible
cd nebula-ansible/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following is an example of inventory.ini.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[all:vars]
# GA or nightly
install_source_type = GA
nebula_version = 2.0.1
os_version = el7
arc = x86_64
pkg = rpm

packages_dir = {{ playbook_dir }}/packages
deploy_dir = /home/vesoft/nebula
data_dir = {{ deploy_dir }}/data

# ssh user
ansible_ssh_user = vesoft

force_download = False

[metad]
192.168.8.[61:63]

[graphd]
192.168.8.[61:63]

[storaged]
192.168.8.[61:63]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Install and deploy Nebula Graph.
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ansible-playbook install.yml
ansible-playbook start.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Monitor hosts
&lt;/h4&gt;

&lt;p&gt;Using docker-compose to deploy a monitoring system is convenient. Docker and Docker-Compose need to be installed on the hosts first.&lt;/p&gt;

&lt;p&gt;Log in 192.168.8.60&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/vesoft-inc/nebula-bench.git

cd nebula-bench
cp -r third/promethues ~/.
cp -r third/exporter ~/.



cd ~/exporter/ &amp;amp;&amp;amp; docker-compose up -d

cd ~/promethues
# Modify the exporter address of monitoring nodes
# vi prometheus.yml
docker-compose up -d

# Copy exporter to 192.168.8.61, 192.168.8.62, and 192.168.8.63, and then start docker-compose
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure the Grafana data source and dashboard. For details, see &lt;a href="https://github.com/vesoft-inc/nebula-bench/tree/master/third"&gt;https://github.com/vesoft-inc/nebula-bench/tree/master/third&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Generate the LDBC dataset
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd nebula-bench

sudo yum install -y git \
                    make \
                    file \
                    libev \
                    libev-devel \
                    gcc \
                    wget \
                    python3 \
                    python3-devel \
                    java-1.8.0-openjdk \
                    maven

pip3 install --user -r requirements.txt

# Using `snb.interactive.1` parameter in ldbc_snb_datagen_hadoop, for more infor https://github.com/ldbc/ldbc_snb_datagen_hadoop/wiki/Configuration

python3 run.py data

# Date generated by mv

mv target/data/test_data/ ./sf1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Import data
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd nebula-bench
# Modify .evn
cp env .env
vi .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following is the example of .env&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATA_FOLDER=sf1
NEBULA_SPACE=sf1
NEBULA_USER=root
NEBULA_PASSWORD=nebula
NEBULA_ADDRESS=192.168.8.61:9669,192.168.8.62:9669,192.168.8.63:9669
#NEBULA_MAX_CONNECTION=100
INFLUXDB_URL=http://192.168.8.60:8086/k6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Compile nebula-importer and K6
./scripts/setup.sh

# Import data
python3 run.py nebula importer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During the import process, you can focus on the following network bandwidth and disk IO writing.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---g5F_oNP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/143537054-5bab1ec9-13cb-4770-be7e-af94314f969f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---g5F_oNP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/143537054-5bab1ec9-13cb-4770-be7e-af94314f969f.png" alt="Import data" width="880" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MGl_xR3W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/143537120-5bb9cf2b-f9d8-4a44-a6df-5dfd2e959ab3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MGl_xR3W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/143537120-5bb9cf2b-f9d8-4a44-a6df-5dfd2e959ab3.png" alt="Import data" width="880" height="206"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Execute the load testing
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python3 run.py stress run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;According to the code source in the file scenarios, the js file will be automatically rendered and K6 will be used to test all scenarios.&lt;/p&gt;

&lt;p&gt;After the execution is over, the js file and the result will be saved in the output folder.&lt;/p&gt;

&lt;p&gt;Among them, latency is the latency time returned by the server, and responseTime is the time from initiating execute to response by the client. The measurement unit is μs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[vesoft@qa-60 nebula-bench]$ more output/result_Go1Step.json
{
    "metrics": {
        "data_sent": {
            "count": 0,
            "rate": 0
        },
        "checks": {
            "passes": 1667632,
            "fails": 0,
            "value": 1
        },
        "data_received": {
            "count": 0,
            "rate": 0
        },
        "iteration_duration": {
            "min": 0.610039,
            "avg": 3.589942336582023,
            "med": 2.9560145,
            "max": 1004.232905,
            "p(90)": 6.351617299999998,
            "p(95)": 7.997563949999995,
            "p(99)": 12.121579809999997
        },
        "latency": {
            "min": 308,
            "avg": 2266.528722763775,
            "med": 1867,
            "p(90)": 3980,
            "p(95)": 5060,
            "p(99)": 7999
        },
        "responseTime": {
            "max": 94030,
            "p(90)": 6177,
            "p(95)": 7778,
            "p(99)": 11616,
            "min": 502,
            "avg": 3437.376111156418,
            "med": 2831
        },
        "iterations": {
            "count": 1667632,
            "rate": 27331.94978169588
        },
        "vus": {
            "max": 100,
            "value": 100,
            "min": 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[vesoft@qa-60 nebula-bench]$ head -300 output/output_Go1Step.csv | grep -v USE
timestamp,nGQL,latency,responseTime,isSucceed,rows,errorMsg
1628147822,GO 1 STEP FROM 4398046516514 OVER KNOWS,1217,1536,true,1,
1628147822,GO 1 STEP FROM 2199023262994 OVER KNOWS,1388,1829,true,94,
1628147822,GO 1 STEP FROM 1129 OVER KNOWS,1488,2875,true,14,
1628147822,GO 1 STEP FROM 6597069771578 OVER KNOWS,1139,1647,true,30,
1628147822,GO 1 STEP FROM 2199023261211 OVER KNOWS,1399,2096,true,6,
1628147822,GO 1 STEP FROM 2199023256684 OVER KNOWS,1377,2202,true,4,
1628147822,GO 1 STEP FROM 4398046515995 OVER KNOWS,1487,2017,true,39,
1628147822,GO 1 STEP FROM 10995116278700 OVER KNOWS,837,1381,true,3,
1628147822,GO 1 STEP FROM 933 OVER KNOWS,1130,3422,true,5,
1628147822,GO 1 STEP FROM 6597069771971 OVER KNOWS,1022,2292,true,60,
1628147822,GO 1 STEP FROM 10995116279952 OVER KNOWS,1221,1758,true,3,
1628147822,GO 1 STEP FROM 8796093031179 OVER KNOWS,1252,1811,true,13,
1628147822,GO 1 STEP FROM 10995116279792 OVER KNOWS,1115,1858,true,6,
1628147822,GO 1 STEP FROM 6597069777326 OVER KNOWS,1223,2016,true,4,
1628147822,GO 1 STEP FROM 8796093028089 OVER KNOWS,1361,2054,true,13,
1628147822,GO 1 STEP FROM 6597069777454 OVER KNOWS,1219,2116,true,2,
1628147822,GO 1 STEP FROM 13194139536109 OVER KNOWS,1027,1604,true,2,
1628147822,GO 1 STEP FROM 10027 OVER KNOWS,2212,3016,true,83,
1628147822,GO 1 STEP FROM 13194139544176 OVER KNOWS,855,1478,true,29,
1628147822,GO 1 STEP FROM 10995116280047 OVER KNOWS,1874,2211,true,12,
1628147822,GO 1 STEP FROM 15393162797860 OVER KNOWS,714,1684,true,5,
1628147822,GO 1 STEP FROM 6597069770517 OVER KNOWS,2295,3056,true,7,
1628147822,GO 1 STEP FROM 17592186050570 OVER KNOWS,768,1630,true,26,
1628147822,GO 1 STEP FROM 8853 OVER KNOWS,2773,3509,true,14,
1628147822,GO 1 STEP FROM 19791209307908 OVER KNOWS,1022,1556,true,6,
1628147822,GO 1 STEP FROM 13194139544258 OVER KNOWS,1542,2309,true,91,
1628147822,GO 1 STEP FROM 10995116285325 OVER KNOWS,1901,2556,true,0,
1628147822,GO 1 STEP FROM 6597069774931 OVER KNOWS,2040,3291,true,152,
1628147822,GO 1 STEP FROM 8796093025056 OVER KNOWS,2007,2728,true,29,
1628147822,GO 1 STEP FROM 21990232560726 OVER KNOWS,1639,2364,true,9,
1628147822,GO 1 STEP FROM 8796093030318 OVER KNOWS,2145,2851,true,6,
1628147822,GO 1 STEP FROM 21990232556027 OVER KNOWS,1784,2554,true,5,
1628147822,GO 1 STEP FROM 15393162796879 OVER KNOWS,2621,3184,true,71,
1628147822,GO 1 STEP FROM 17592186051113 OVER KNOWS,2052,2990,true,5,
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is also possible to execute the load testing in one scenario and continuously adjust the configuration parameters for comparison.&lt;/p&gt;

&lt;h4&gt;
  
  
  Concurrent reading
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#Run Go2Step with 50 virtual users and 300 seconds of duration
python3 run.py stress run -scenario go.Go2Step -vu 50 -d 300
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFO[0302] 2021/08/06 03:55:27 [INFO] finish init the pool

     ✓ IsSucceed

     █ setup

     █ teardown

     checks...............: 100.00% ✓ 1559930     ✗ 0
     data_received........: 0 B     0 B/s
     data_sent............: 0 B     0 B/s
     iteration_duration...: min=687.47µs avg=9.6ms       med=8.04ms max=1.03s  p(90)=18.41ms p(95)=22.58ms p(99)=31.87ms
     iterations...........: 1559930 5181.432199/s
     latency..............: min=398      avg=6847.850345 med=5736   max=222542 p(90)=13046   p(95)=16217   p(99)=23448
     responseTime.........: min=603      avg=9460.857877 med=7904   max=226992 p(90)=18262   p(95)=22429   p(99)=31726.71
     vus..................: 50      min=0         max=50
     vus_max..............: 50      min=50        max=50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every metric can be monitored at the same time.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DRZiU7B7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/143537715-f4f404b6-e842-4904-94f6-51aaaf4ef129.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DRZiU7B7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/143537715-f4f404b6-e842-4904-94f6-51aaaf4ef129.png" alt="Concurrent reading" width="880" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;checks&lt;/code&gt; is to verify whether the request is executed successfully. If the execution fails, the failed message will be saved in the CSV file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awk -F ',' '{print $NF}' output/output_Go2Step.csv|sort |uniq -c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Execute Go2Step with 200 virtual users and 300 seconds of duration
python3 run.py stress run -scenario go.Go2Step -vu 200 -d 300
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFO[0302] 2021/08/06 04:02:34 [INFO] finish init the pool

     ✓ IsSucceed

     █ setup

     █ teardown

     checks...............: 100.00% ✓ 1866850    ✗ 0
     data_received........: 0 B     0 B/s
     data_sent............: 0 B     0 B/s
     iteration_duration...: min=724.77µs avg=32.12ms      med=25.56ms max=1.03s  p(90)=63.07ms p(95)=84.52ms  p(99)=123.92ms
     iterations...........: 1866850 6200.23481/s
     latency..............: min=395      avg=25280.893558 med=20411   max=312781 p(90)=48673   p(95)=64758    p(99)=97993.53
     responseTime.........: min=627      avg=31970.234329 med=25400   max=340299 p(90)=62907   p(95)=84361.55 p(99)=123750
     vus..................: 200     min=0        max=200
     vus_max..............: 200     min=200      max=200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;K6 metrics to be monitored with Grafana&lt;br&gt;
![Concurrent reading](&lt;a href="https://user-images.githubusercontent.com/90186547/143537954-780fade2-ae2a-4882-a33e-3df47ad68402.png%EF%BC%89"&gt;https://user-images.githubusercontent.com/90186547/143537954-780fade2-ae2a-4882-a33e-3df47ad68402.png）&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Concurrent writing
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#Execute insert with 200 virtual users and 300 seconds of duration. By default, batchSize is 100.

python3 run.py stress run -scenario go.Go2Step -vu 200 -d 300
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The js file can be modified manually to adjust batchSize&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sed -i 's/batchSize = 100/batchSize = 300/g' output/InsertPersonScenario.js

# Run K6 manually

scripts/k6 run output/InsertPersonScenario.js -u 400 -d 30s --summary-trend-stats "min,avg,med,max,p(90),p(95),p(99)" --summary-export output/result_InsertPersonScenario.json --out influxdb=http://192.168.8.60:8086/k6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the batchSize is 300 with 400 virtual users, an error will be returned.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFO[0032] 2021/08/06 04:03:49 [INFO] finish init the pool

     ✗ IsSucceed
      ↳  96% — ✓ 31257 / ✗ 1103

     █ setup

     █ teardown

     checks...............: 96.59% ✓ 31257       ✗ 1103
     data_received........: 0 B    0 B/s
     data_sent............: 0 B    0 B/s
     iteration_duration...: min=12.56ms avg=360.11ms      med=319.12ms max=2.07s   p(90)=590.31ms p(95)=696.69ms p(99)=958.32ms
     iterations...........: 32360  1028.339207/s
     latency..............: min=4642    avg=206931.543016 med=206162   max=915671  p(90)=320397.4 p(95)=355798.7 p(99)=459521.39
     responseTime.........: min=6272    avg=250383.122188 med=239297.5 max=1497159 p(90)=384190.5 p(95)=443439.6 p(99)=631460.92
     vus..................: 400    min=0         max=400
     vus_max..............: 400    min=400       max=400
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awk -F ',' '{print $NF}' output/output_InsertPersonScenario.csv|sort |uniq -c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; 31660
   1103  error: E_CONSENSUS_ERROR(-16)."
      1 errorMsg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If E_CONSENSUS_ERROR occurs, it should be that the appendlog buffer of raft is overflown when the concurrency is large, which can be solved by adjusting relevant parameters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The load testing uses the LDBC dataset standard to ensure data uniform. Even when bigger data volume, say one billion vertices, is generated, the graph schema is the same.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;K6 is more convenient than Jmeter for the load testing. For more details, please refer &lt;a href="https://k6.io/docs/"&gt;https://k6.io/docs/&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can easily find the bottleneck of the system resources by simulating various scenarios or adjust parameters in Nebula Graph with the mentioned tools.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>opensource</category>
      <category>database</category>
      <category>devops</category>
      <category>programming</category>
    </item>
    <item>
      <title>TOSS: 1 Secret to Achieve Eventual Consistency of Edges in Nebula Graph</title>
      <dc:creator>lisahui</dc:creator>
      <pubDate>Thu, 11 Nov 2021 07:35:27 +0000</pubDate>
      <link>https://dev.to/lisahui/toss-1-secret-to-achieve-eventual-consistency-of-edges-in-nebula-graph-1h92</link>
      <guid>https://dev.to/lisahui/toss-1-secret-to-achieve-eventual-consistency-of-edges-in-nebula-graph-1h92</guid>
      <description>&lt;p&gt;Nebula Graph has just released v.2.6. In this version, TOSS is certainly one of the important features. Here a detailed explanation about TOSS will be given.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let’s start from a GO statement
&lt;/h3&gt;

&lt;p&gt;As we all know, there are two types of edges, directed and undirected edges. When traversing directed edges, you can traverse forward or reversely. Nebula Graph also supports this semantics. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go from "101" over known reversely yield known.kdate, id($$);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above statement starts from Vertex 101 to find all the corresponding neighbors reversely. However, when you insert an edge into Nebula Graph, the command will be like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;insert edge known(degree) VALUES "100" -&amp;gt; "101":(299792458);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Seemingly, the above statement only specifies the outgoing edge. This is because Nebula Graph will specify the incoming edge in the background when you insert an edge.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to insert an edge into Nebula Graph
&lt;/h3&gt;

&lt;p&gt;Take the INSERT statement above as an example, the background execution process contains the following:&lt;/p&gt;

&lt;p&gt;Nebula Console sends the INSERT request to the Nebula Graph server.&lt;/p&gt;

&lt;p&gt;After the Nebula Graph server receives the request, it adds an incoming edge for each outgoing edge and sends AddEdgeRequest to their hosts respectively.&lt;/p&gt;

&lt;p&gt;After the host (Nebula Storage server) receives AddEdgeRequest, it inserts the edge locally (via the Raft protocol) and returns the result to the Nebula Graph server.&lt;/p&gt;

&lt;p&gt;The Nebula Graph server then returns the results from both hosts to the Nebula Console for querying purpose.&lt;/p&gt;

&lt;p&gt;The flow diagram is as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yTDIF2J_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/141245971-9d62e48b-7646-4d9c-a929-d8ac7d45c737.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yTDIF2J_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/141245971-9d62e48b-7646-4d9c-a929-d8ac7d45c737.png" alt="How to insert an edge into Nebula Graph" width="880" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are familiar with network/distributed programming, you may see the problem now. The graph service uses RPC to call both storage services. When the INSERT operation is executed enough times, one RPC succeeds while the other fails due to timeout. In other words, an INSERT operation may succeed on the outgoing edge while fail on the incoming-edge.&lt;/p&gt;

&lt;p&gt;If now a user requires consistent property settings for both outgoing edge and incoming edge, the client has to retry the query infinitely. It is inappropriate for Nebula Graph, s a database product, to rely on the client for data atomicity.&lt;/p&gt;

&lt;p&gt;A requirement thus comes into being, that is, to ensure the atomicity of outgoing edge and incoming edge. This means that the outgoing edge and incoming edge should be updated either successfully or they should fail at the same time. And TOSS (Transaction On Storage Side) is the feature to ensure the eventual consistency of edges upon INSERT, UPDATE, and UPSERT operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to use TOSS
&lt;/h3&gt;

&lt;p&gt;With the release of Nebula Graph v2.6.0, the TOSS function has also been launched. The feature is set to Disabled by default due to performance and stability considerations. You can find the enable_experimental_feature option in the Nebula Graph server configuration file and set it to True. Then you need to restart the graphd service for the feature to take effect. The command is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;--enable_experimental_feature=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the operations CREATE SPACE / CREATE EDGE / INSERT / UPDATE will achieve eventual consistency of edges in Nebula Graph. (Just execute the operations as before)&lt;/p&gt;

&lt;p&gt;Note: The TOSS feature will be only applied to incremental data after it is enabled.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>database</category>
      <category>devops</category>
      <category>programming</category>
    </item>
    <item>
      <title>1 Article tells how Nebula Clients work with fbthrift</title>
      <dc:creator>lisahui</dc:creator>
      <pubDate>Wed, 03 Nov 2021 03:44:41 +0000</pubDate>
      <link>https://dev.to/lisahui/how-nebula-clients-works-with-fbthrift-hjg</link>
      <guid>https://dev.to/lisahui/how-nebula-clients-works-with-fbthrift-hjg</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Nebula Clients provide users with APIs in multiple programming languages to interact with Nebula Graph and repackages the data structure returned by the server for better use.&lt;/p&gt;

&lt;p&gt;Currently, Nebula Clients support C++, Java, Python, Golang, and Rust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Framework for service communication
&lt;/h2&gt;

&lt;p&gt;Nebula Clients use fbthrift &lt;a href="https://github.com/facebook/fbthrift"&gt;https://github.com/facebook/fbthrift&lt;/a&gt; as the RPC framework for service communication between servers and clients to implement cross-language interaction.&lt;/p&gt;

&lt;p&gt;At a high level, fbthrift is:&lt;/p&gt;

&lt;p&gt;A code generator: fbthrift has a code generator that generates data structures that can be serialized using Thrift in different languages.&lt;br&gt;
A serialization framework: fbthrift has a set of protocols to serialize the generated structures created from the code generator.&lt;br&gt;
An RPC framework: fbthrift has a framework to send messages between clients and servers and to call application-defined functions when receiving messages in different languages.&lt;/p&gt;
&lt;h2&gt;
  
  
  Examples
&lt;/h2&gt;

&lt;p&gt;Take the Golang client as an example to show the application of fbthrift in Nebula Graph.&lt;/p&gt;

&lt;p&gt;The definition of the Vertex structure in servers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;struct Vertex {
    Value vid;
    std::vector&amp;lt;Tag&amp;gt; tags;

    Vertex() = default;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Define some data structures in src/interface/common.thrift:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;struct Tag {
        1: binary name,
        // List of &amp;lt;prop_name, prop_value&amp;gt;
        2: map&amp;lt;binary, Value&amp;gt; (cpp.template = "std::unordered_map") props,
} (cpp.type = "nebula::Tag")

struct Vertex {
        1: Value     vid,
        2: list&amp;lt;Tag&amp;gt; tags,
} (cpp.type = "nebula::Vertex")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above example, we define a Vertex structure. (cpp.type = "nebula::Vertex") indicates this structure corresponds to the nebula::Vertex of the server.&lt;/p&gt;

&lt;p&gt;fbthrift will automatically generate the data structure in Golang:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Attributes:
//  - Vid
//  - Tags
type Vertex struct {
    Vid *Value `thrift:"vid,1" db:"vid" json:"vid"`
    Tags []*Tag `thrift:"tags,2" db:"tags" json:"tags"`
}

func NewVertex() *Vertex {
    return &amp;amp;Vertex{}
}

...

func (p *Vertex) Read(iprot thrift.Protocol) error { // Deserialization
    ...
}

func (p *Vertex) Write(oprot thrift.Protocol) error { // Serialization 
    ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In MATCH (v:Person) WHERE id(v) == "ABC" RETURN v, the client requests a vertex (nebula::Vertex) from the server. The server will serialize it after finding it. After the server finds this vertex, it will be serialized and sent to the client through the transport of the RPC communication framework. When the client receives this data, it will be deserialized to generate the corresponding data structure (type Vertex struct) defined in the client.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clients
&lt;/h2&gt;

&lt;p&gt;In this section, we will take nebula-go as an example to introduce different modules of the client and their main interfaces.&lt;/p&gt;

&lt;p&gt;Configs provides the whole configuration options.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type PoolConfig struct {
    // Set the timeout threshold. The default value 0 means it does not time out. Unit: ms
    TimeOut time.Duration
    // The maximum idle time of each connection. When the idle time exceeds this threshold, the connection will be disconnected and deleted. The default value 0 means permanently idle and the connection will not be disconnected
    IdleTime time.Duration
    // max_connection_pool_size: Set the maximum number of connections in the connection pool. The default value is 10
    MaxConnPoolSize int
    // The minimum number of idle connections. The default value is 0
    MinConnPoolSize int
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Session provides an interface for users to call directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Manage the specific information of Session
type Session struct {
    // Use for identity verification or message retry when executing commands
    sessionID  int64
    // Currently held connections
    connection *connection
    // Currently used connection pools
    connPool   *ConnectionPool
    // Log tools
    log        Logger
    // Use to save the time zone used by the current session
    timezoneInfo
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The definition of interfaces is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Execute nGQL. The return data type is ResultSet. This interface is non-thread-safe
    func (session *Session) Execute(stmt string) (*ResultSet, error) {...}
    // Re-acquire a connection from the connection pool for the current Session
    func (session *Session) reConnect() error {...}
    // Signout, release the Session ID, and return the connection to the pool
    func (session *Session) Release() {
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ConnectionPool manages all connections. The main interfaces are as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Create a new connection pool and complete the initialization with the entered service address
func NewConnectionPool(addresses []HostAddress, conf PoolConfig, log Logger) (*ConnectionPool, error) {...}
// Validate and get the Session example
func (pool *ConnectionPool) GetSession(username, password string) (*Session, error) {...}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Connection packages the network of thrift and provides the following interfaces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Establish a connection with the specified ip and port
func (cn *connection) open(hostAddress HostAddress, timeout time.Duration) error {...}
// Authenticate the username and password
func (cn *connection) authenticate(username, password string) (*graph.AuthResponse, error) {
// Execute query
func (cn *connection) execute(sessionID int64, stmt string) (*graph.ExecutionResponse, error) {...}
// Generate a temp sessionID 0 and send the query "YIELD 1" to test if the connection is usable.
func (cn *connection) ping() bool {...}
// Release sessionId to the graphd process.
func (cn *connection) signOut(sessionID int64) error {...}
// Disconnect.
func (cn *connection) close() {...}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;LoadBalance is used in the connection pool.&lt;br&gt;
Policy: Polling&lt;/p&gt;
&lt;h2&gt;
  
  
  Interaction of modules
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ANwViQLn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/139806844-7bbffd66-6161-40d4-9115-e7522f44538d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ANwViQLn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/139806844-7bbffd66-6161-40d4-9115-e7522f44538d.png" alt="Interaction of modules" width="880" height="486"&gt;&lt;/a&gt;&lt;br&gt;
Connection pool&lt;/p&gt;
&lt;h3&gt;
  
  
  Initialize:
&lt;/h3&gt;

&lt;p&gt;When using it, the user needs to create and initialize a connection pool. During initialization, the connection pool will establish a connection at the address of the Nebula service specified by the user. If multiple Graph services are deployed in a cluster deployment method, the connection pool will use a polling policy to balance the load and establish a nearly equal number of connections for each address.&lt;br&gt;
Manage connections:&lt;br&gt;
Two queues are maintained in the connection pool, idle connection queue and active Connection Queue. The connection pool will periodically detect expired idle connections and close them. These two queues will use read-write lock to ensure the correctness of multi-thread execution when adding or deleting elements.&lt;br&gt;
When Session requests a connection to the connection pool, it will check whether there are usable connections in the idle connection queue. If there are any usable connections, they will be directly returned to the Session for users to use. If there are no usable connections and the current total number of connections does not exceed the maximum number of connections defined in the configuration, a new connection is created to the Session. If it reaches the maximum number of connections, an error is returned.&lt;br&gt;
Generally, the connection pool needs to be closed only when you close the program. All connections in the pool will be disconnected when the program is closed.&lt;/p&gt;
&lt;h3&gt;
  
  
  Session
&lt;/h3&gt;

&lt;p&gt;Session is generated through the connection pool. The user needs to provide the password for authentication. After the authentication succeeds, the user will get a Session example and communicate with the server through the connection in the Session. The most commonly used interface is execute(). If an error occurs during execution, the client will check the error type. If it is a network error, it will automatically reconnect and try to execute the statement again.&lt;br&gt;
Note that a Session does not support being used by multiple threads at the same time. The correct way is that multiple sessions are applied by multiple threads, and one session is used by each thread.&lt;br&gt;
When the Session is released, the connection held by it will be put back into the idle connection queue of the connection pool so that it can be reused by other sessions later.&lt;/p&gt;
&lt;h3&gt;
  
  
  Connection
&lt;/h3&gt;

&lt;p&gt;Each connection example is equivalent and can be held by any Session. The purpose of this design is to allow these connections to be reused by different Sessions, reducing repeatedly enabling and disabling Transport.&lt;br&gt;
The connection will send the client’s request to the server and return the result to the Session.&lt;/p&gt;
&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Initialize connection pool
pool, err := nebula.NewConnectionPool(hostList, testPoolConfig, log)
if err != nil {
    log.Fatal(fmt.Sprintf("Fail to initialize the connection pool, host: %s, port: %d, %s", address, port, err.Error()))
}
// Close all connections in the pool when program exits
defer pool.Close()

// Create session
session, err := pool.GetSession(username, password)
if err != nil {
    log.Fatal(fmt.Sprintf("Fail to create a new session from connection pool, username: %s, password: %s, %s",
        username, password, err.Error()))
}
// Release session and return connection back to connection pool when program exits
defer session.Release()

// Excute a query
resultSet, err := session.Execute(query)
if err != nil {
    fmt.Print(err.Error())
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Returned data structure
&lt;/h2&gt;

&lt;p&gt;The client packages the returned query results by part of the complex servers and adds an interface for convenience use.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WDZ_zJIp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1400/1%2A5n3-d30WiZMNr8jRc3WeTQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WDZ_zJIp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://miro.medium.com/max/1400/1%2A5n3-d30WiZMNr8jRc3WeTQ.png" alt="Returned data structure" width="880" height="837"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;nebula::Value will be packaged as ValueWrapper in the client and converted to other structures through interfaces. (i.g. node = ValueWrapper.asNode())&lt;/p&gt;
&lt;h2&gt;
  
  
  Analysis of data structure
&lt;/h2&gt;

&lt;p&gt;For MATCH p= (v:player{name:"Tim Duncan"})-[]-&amp;gt;(v2) RETURN p, the returned result is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| p                                                                                                                                                                                                                         |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| &amp;lt;("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})&amp;lt;-[:teammate@0 {end_year: 2016, start_year: 2002}]-("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"})&amp;gt; |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
Got 1 rows (time spent 11550/12009 us)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can see that the returned result contains one row, and its type is a path. At this time, you can execute as follows to get the properties of the destination vertex of the path (v2).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Excute a query
resultSet, _ := session.Execute("MATCH p= (v:player{name:"\"Tim Duncan"\"})-[]-&amp;gt;(v2) RETURN p")

// Get the first row of the result. The index of the first row is 0
record, err := resultSet.GetRowValuesByIndex(0)
if err != nil {
    t.Fatalf(err.Error())
}

// Take the value of the cell in the first column from the first row
// At this time, the type of valInCol0 is ValueWrapper
valInCol0, err := record.GetValueByIndex(0)

// Convert ValueWrapper into PathWrapper objects.
pathWrap, err = valInCol0.AsPath()

// Get the destination vertex through pathWrap.GetEndNode()
node, err = pathWrap.GetEndNode()

// Get all properties through node.Properties()
// The type of props is map[string]*ValueWrapper
props, err = node.Properties()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Address of clients
&lt;/h2&gt;

&lt;p&gt;The GitHub addresses of clients are as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/vesoft-inc/nebula-cpp"&gt;https://github.com/vesoft-inc/nebula-cpp&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/vesoft-inc/nebula-java"&gt;https://github.com/vesoft-inc/nebula-java&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/vesoft-inc/nebula-python"&gt;https://github.com/vesoft-inc/nebula-python&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/vesoft-inc/nebula-go"&gt;https://github.com/vesoft-inc/nebula-go&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/vesoft-inc/nebula-rust"&gt;https://github.com/vesoft-inc/nebula-rust&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you encounter any problems in the process of using Nebula Graph, please refer to &lt;a&gt;Nebula Graph Database Manual&lt;/a&gt; to troubleshoot the problem. It records in detail the knowledge points and specific usage of the graph database and the graph database Nebula Graph.&lt;/p&gt;

</description>
      <category>database</category>
      <category>opensource</category>
      <category>devops</category>
      <category>programming</category>
    </item>
    <item>
      <title>Nebula Explorer: A Tool to Visualize Graph Data Easily</title>
      <dc:creator>lisahui</dc:creator>
      <pubDate>Thu, 28 Oct 2021 06:11:10 +0000</pubDate>
      <link>https://dev.to/lisahui/nebula-explorer-a-tool-to-visualize-graph-data-easily-1761</link>
      <guid>https://dev.to/lisahui/nebula-explorer-a-tool-to-visualize-graph-data-easily-1761</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gqOqZrpE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/139035653-d03f7c75-f6b2-47eb-ac55-a32351c6429d.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gqOqZrpE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/139035653-d03f7c75-f6b2-47eb-ac55-a32351c6429d.jpg" alt="Nebula Explorer" width="880" height="375"&gt;&lt;/a&gt;&lt;br&gt;
Nebula Explorer is a visualization tool of the Nebula Graph ecosystem. With it, you can easily access Nebula Graph, and then query and retrieve graph data via GUI without having to learn nGQL. It can display graph data as a graph on a canvas for you to visually analyze data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Querying Graph Data
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8je8x-pL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/139034825-447c58a9-27d8-40fa-84ec-31cb0a018a95.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8je8x-pL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/139034825-447c58a9-27d8-40fa-84ec-31cb0a018a95.png" alt="Querying Graph Data" width="612" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nebula Explorer provides various methods for querying graph data. For example, you can specify a VID list or tags (with their indexes) to query vertices, or specify one or more VIDs to query a subgraph. When the queried vertices are retrieved, you can select one or more for further exploration. For example, you can query for the shared neighbors and the related paths of each pair of vertices. Through progressive queries, you can improve and enrich the data on the canvas for further graph analysis.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4WMZEpDC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/139036362-29b7520c-6e59-46b1-8d6a-a9802cadc511.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4WMZEpDC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/139036362-29b7520c-6e59-46b1-8d6a-a9802cadc511.png" alt="Querying Graph Data" width="612" height="324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Displaying Graph Data
&lt;/h2&gt;

&lt;p&gt;When the graph data is retrieved, you can manipulate it on the canvas flexibly, including drag-and-drop, zoom-in or zoom-out, click-and-drag, and marking vertices with colors or icons. Nebula Explorer enables you to have fun with the graph data model by providing various features.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LcssoIUH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/139036868-adcac1fe-8b97-4acb-8e69-25af5a936ace.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LcssoIUH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/90186547/139036868-adcac1fe-8b97-4acb-8e69-25af5a936ace.png" alt="Displaying Graph Data" width="612" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Roadmap
&lt;/h2&gt;

&lt;p&gt;Besides querying and exploring data, Nebula Explorer will support graph computing in the future, which will enable the Nebula Graph users to use appropriate methods to query and analyze graph data in various business scenarios.&lt;/p&gt;

&lt;p&gt;Currently, Nebula Explorer is only available for Enterprise users. If you are interested, please send an email with “Require Dashboard” as the subject to &lt;a href="mailto:inquiry@vesoft.com"&gt;inquiry@vesoft.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you encounter any problems in the process of using Nebula Graph, please refer to &lt;a&gt;Nebula Graph Database Manual&lt;/a&gt; to troubleshoot the problem. It records in detail the knowledge points and specific usage of the graph database and the graph database Nebula Graph.&lt;/p&gt;

</description>
      <category>database</category>
      <category>opensource</category>
      <category>devops</category>
      <category>programming</category>
    </item>
    <item>
      <title> 1 way tells how an execution plan is generated</title>
      <dc:creator>lisahui</dc:creator>
      <pubDate>Fri, 22 Oct 2021 08:16:00 +0000</pubDate>
      <link>https://dev.to/lisahui/1-way-tells-how-an-execution-plan-is-generated-4k9k</link>
      <guid>https://dev.to/lisahui/1-way-tells-how-an-execution-plan-is-generated-4k9k</guid>
      <description>&lt;p&gt;In the last article, we mentioned that Validator will convert an AST generated by Parser to an execution plan. In this article, we will explain how an execution plan is generated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F136778731-a9ea1b31-55b6-4b4c-8968-6f53baacb979.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F136778731-a9ea1b31-55b6-4b4c-8968-6f53baacb979.png" alt="Nebula Graph Source Code Explained: Planner"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Planner is an execution plan generator. It generates an execution plan based on the semantically valid AST that was validated by Validator, and then passes the plan to Optimizer to generate an optimized execution plan. Finally, Executor will execute the optimized plan. An execution plan is composed of a series of nodes (PlanNode).&lt;/p&gt;

&lt;h2&gt;
  
  
  Structure of Source Files
&lt;/h2&gt;

&lt;p&gt;Here is the structure of source files for Planner.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/planner
├── CMakeLists.txt
├── match/
├── ngql/
├── plan/
├── Planner.cpp
├── Planner.h
├── PlannersRegister.cpp
├── PlannersRegister.h
├── SequentialPlanner.cpp
├── SequentialPlanner.h
└── test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Planner.h file defines the data structure of SubPlan and the interfaces of Planner.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;struct SubPlan {
    // root and tail of a subplan.
    PlanNode*   root{nullptr};
    PlanNode*   tail{nullptr};
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PlannersRegister is responsible for registering available planners. So far, SequentialPlanner, PathPlanner, LookupPlanner, GoPlanner, and MatchPlanner have been registered for Nebula Graph.&lt;/p&gt;

&lt;p&gt;The corresponding sentence of SequentialPlanner is SequentialSentences, which is a combined sentence composed of multiple sentences separated with semicolons. Each sentence can be a GO, LOOKUP, or MATCH statement. Therefore, SequentialPlanner generates multiple execution plans by calling other sentence planners and then calling Validator::appendPlan to connect the plans end to end.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F136778879-98d71176-15c1-4aff-a7f1-9a03f7d49556.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F136778879-98d71176-15c1-4aff-a7f1-9a03f7d49556.png" alt="Nebula Graph Source Code Explained: Planner"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The match/ directory defines the planners and connection strategies of SubPlans of some statements and clauses compatible with openCypher, such as MATCH, UNWIND, WITH, RETURN, WHERE, ORDER BY, SKIP, and LIMIT. SegmentsConnector uses an appropriate strategy, such as AddInput, addDependency, or innerJoinSegments, to connect the SubPlans end to end to generate a complete execution plan.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/planner/match
├── AddDependencyStrategy.cpp
├── AddDependencyStrategy.h
├── AddInputStrategy.cpp
├── AddInputStrategy.h
├── CartesianProductStrategy.cpp
├── CartesianProductStrategy.h
├── CypherClausePlanner.h
├── EdgeIndexSeek.h
├── Expand.cpp
├── Expand.h
├── InnerJoinStrategy.cpp
├── InnerJoinStrategy.h
├── LabelIndexSeek.cpp
├── LabelIndexSeek.h
├── LeftOuterJoinStrategy.h
├── MatchClausePlanner.cpp
├── MatchClausePlanner.h
├── MatchPlanner.cpp
├── MatchPlanner.h
├── MatchSolver.cpp
├── MatchSolver.h
├── OrderByClausePlanner.cpp
├── OrderByClausePlanner.h
├── PaginationPlanner.cpp
├── PaginationPlanner.h
├── PropIndexSeek.cpp
├── PropIndexSeek.h
├── ReturnClausePlanner.cpp
├── ReturnClausePlanner.h
├── SegmentsConnector.cpp
├── SegmentsConnector.h
├── SegmentsConnectStrategy.h
├── StartVidFinder.cpp
├── StartVidFinder.h
├── UnionStrategy.h
├── UnwindClausePlanner.cpp
├── UnwindClausePlanner.h
├── VertexIdSeek.cpp
├── VertexIdSeek.h
├── WhereClausePlanner.cpp
├── WhereClausePlanner.h
├── WithClausePlanner.cpp
├── WithClausePlanner.h
├── YieldClausePlanner.cpp
└── YieldClausePlanner.h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ngql/ directory defines the planners of nGQL statements such as GO, LOOKUP, and FIND PATH.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/planner/ngql
├── GoPlanner.cpp
├── GoPlanner.h
├── LookupPlanner.cpp
├── LookupPlanner.h
├── PathPlanner.cpp
└── PathPlanner.h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The plan/ directory defines seven categories, with a total of more than 100 plan nodes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/planner/plan
├── Admin.cpp
├── Admin.h
├── Algo.cpp
├── Algo.h
├── ExecutionPlan.cpp
├── ExecutionPlan.h
├── Logic.cpp
├── Logic.h
├── Maintain.cpp
├── Maintain.h
├── Mutate.cpp
├── Mutate.h
├── PlanNode.cpp
├── PlanNode.h
├── Query.cpp
├── Query.h
└── Scan.h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is an introduction to the purpose of plan nodes:&lt;/p&gt;

&lt;p&gt;Admin: For the nodes related to database administration.&lt;br&gt;
Algo: For the nodes related to the algorithms of paths, subgraphs, and so on.&lt;br&gt;
Logic: For the nodes related to logic controlling, such as loop and binary selection.&lt;br&gt;
Maintain: For the nodes related to schema.&lt;br&gt;
Mutate: For the nodes related to DML.&lt;br&gt;
Query: For the nodes related to query computation.&lt;br&gt;
Scan: For the nodes related to indexing and scanning.&lt;br&gt;
In the Executor phase, each PlanNode generates an executor, and each executor is responsible for a specific functionality.&lt;/p&gt;

&lt;p&gt;For example, here is the source code of the GetNeighbors node.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;static GetNeighbors* make(QueryContext* qctx,
                              PlanNode* input,
                              GraphSpaceID space,
                              Expression* src,
                              std::vector&amp;lt;EdgeType&amp;gt; edgeTypes,
                              Direction edgeDirection,
                              std::unique_ptr&amp;lt;std::vector&amp;lt;VertexProp&amp;gt;&amp;gt;&amp;amp;&amp;amp; vertexProps,
                              std::unique_ptr&amp;lt;std::vector&amp;lt;EdgeProp&amp;gt;&amp;gt;&amp;amp;&amp;amp; edgeProps,
                              std::unique_ptr&amp;lt;std::vector&amp;lt;StatProp&amp;gt;&amp;gt;&amp;amp;&amp;amp; statProps,
                              std::unique_ptr&amp;lt;std::vector&amp;lt;Expr&amp;gt;&amp;gt;&amp;amp;&amp;amp; exprs,
                              bool dedup = false,
                              bool random = false,
                              std::vector&amp;lt;storage::cpp2::OrderBy&amp;gt; orderBy = {},
                              int64_t limit = -1,
                              std::string filter = "")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GetNeighbors is the semantic encapsulation of the KV of an edge in the storage layer. Based on the source vertex of the given edge type, it will find the destination vertex of an edge. During the finding edge course, GetNeighbors can retrieve the properties of the edge (edgeProps). Additionally, the outgoing edge is stored with its source vertex in one partition (shard), so the properties of the source vertex (vertexProps) can be retrieved easily.&lt;/p&gt;

&lt;p&gt;Here is the source code of the Aggregate node.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;static Aggregate* make(QueryContext* qctx,
                               PlanNode* input, 
                               std::vector&amp;lt;Expression*&amp;gt;&amp;amp;&amp;amp; groupKeys = {},
                               std::vector&amp;lt;Expression*&amp;gt;&amp;amp;&amp;amp; groupItems = {})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Aggregate node is for aggregate computing. It groups the table according to groupKeys, and does aggregate calculation on groupItems.&lt;/p&gt;

&lt;p&gt;Here is the source code of the Loop node.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;static Loop* make(QueryContext* qctx,
                      PlanNode* input,
                      PlanNode* body = nullptr,
                      Expression* condition = nullptr);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Loop node is for looping. It keeps on executing the PlanNode segement between the body and the next Start node until the value of condition is changed to false.&lt;/p&gt;

&lt;p&gt;Here is the source code of the InnerJoin node.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;static InnerJoin* make(QueryContext* qctx,
                           PlanNode* input,
                           std::pair&amp;lt;std::string, int64_t&amp;gt; leftVar,
                           std::pair&amp;lt;std::string, int64_t&amp;gt; rightVar,
                           std::vector&amp;lt;Expression*&amp;gt; hashKeys = {},
                           std::vector&amp;lt;Expression*&amp;gt; probeKeys = {})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The InnerJoin node aims to perform inner join between two tables (Table or DataSet). leftVar and rightVar refer to the two tables respectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entry Functions
&lt;/h2&gt;

&lt;p&gt;The entry function of Planner is Validator∷toPlan().&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Status Validator::toPlan() {
    auto* astCtx = getAstContext();
    if (astCtx != nullptr) {
        astCtx-&amp;gt;space = space_;
    }
    auto subPlanStatus = Planner::toPlan(astCtx);
    NG_RETURN_IF_ERROR(subPlanStatus);
    auto subPlan = std::move(subPlanStatus).value();
    root_ = subPlan.root;
    tail_ = subPlan.tail;
    VLOG(1) &amp;lt;&amp;lt; "root: " &amp;lt;&amp;lt; root_-&amp;gt;kind() &amp;lt;&amp;lt; " tail: " &amp;lt;&amp;lt; tail_-&amp;gt;kind();
    return Status::OK();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Steps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Calling getAstContext()
Firstly, getAstContext() is called to obtain the validated (by Validator) and rewritten AST contexts. The data structure of these contexts are defined in src/context/.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/context/ast
├── AstContext.h
├── CypherAstContext.h
└── QueryAstContext.h
struct AstContext {
    QueryContext*   qctx; // The context of each query request
    Sentence*       sentence; // The AST of each query statement
    SpaceInfo       space; // The current graph space
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CypherAstContext defines the AST contexts of the openCypher compatible statements. QueryAstContext defines the AST contexts of the nGQL statements.&lt;/p&gt;

&lt;p&gt;2.Calling Planner::toPlan(astCtx)&lt;br&gt;
Secondly, Planner∷toPlan(astCtx) is called. Based on the AST contexts, it will find the registered planners for the query statement in PlannerMap, and then the corresponding execution plan is generated.&lt;/p&gt;

&lt;p&gt;Each plan is composed of a series of PlanNodes. There are two major relationships between PlanNodes, execution dependency and data dependency.&lt;/p&gt;

&lt;p&gt;Execution dependency: From the perspective of execution order, an execution plan is a directed acyclic graph, and the dependencies between nodes are determined when the plan is generated. In the execution phase, the executor generates an operator for each node, and starts scheduling from the root node. If the root node is found dependent on another node, a recursive calling is executed for the node that the root node depends on. The process repeats until it finds a node that is not dependent on any other nodes. And then, the node is executed. After the execution is done, the executor will continue to execute the nodes that depend on it until the root node is reached.&lt;br&gt;
Data dependency: The data dependency between nodes is like the execution dependency, that is, the output of the previous execution is the input of the next execution. Let’s take the InnerJoin node as an example. The inputs of InnerJoin may be the outputs of some nodes that are not adjacent to it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F136779022-35344f18-5334-4d2b-ac6b-4deadaccc3c9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F136779022-35344f18-5334-4d2b-ac6b-4deadaccc3c9.png" alt="Nebula Graph Source Code Explained: Planner"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(In the preceding figure, the solid lines represent the execution dependencies and the dashed lines represent the data dependencies.)&lt;/p&gt;
&lt;h2&gt;
  
  
  An Example
&lt;/h2&gt;

&lt;p&gt;In this section, I will take MatchPlanner as an example to show how an execution plan is generated.&lt;/p&gt;

&lt;p&gt;Here is the example statement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MATCH (v:player)-[:like*2..4]-(v2:player)\
WITH v, v2.age AS age ORDER BY age WHERE age &amp;gt; 18\
RETURN id(v), age
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After validated by MatchValidator and rewritten, this statement will be output as a tree composed of contexts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F136779116-e5ee2fd9-bd24-4532-8b9f-cd63bad8dd7a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F136779116-e5ee2fd9-bd24-4532-8b9f-cd63bad8dd7a.png" alt="Nebula Graph Source Code Explained: Planner"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;=&amp;gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F136779268-49f6dc23-7e66-4f20-b96c-5290d50e22a7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F136779268-49f6dc23-7e66-4f20-b96c-5290d50e22a7.png" alt="Nebula Graph Source Code Explained: Planner"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each context corresponds to a clause or a subclause.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;enum class CypherClauseKind : uint8_t {
    kMatch,
    kUnwind,
    kWith,
    kWhere,
    kReturn,
    kOrderBy,
    kPagination,
    kYield,
};

struct CypherClauseContextBase : AstContext {
    explicit CypherClauseContextBase(CypherClauseKind k) : kind(k) {}
    virtual ~CypherClauseContextBase() = default;

    const CypherClauseKind  kind;
};

struct MatchClauseContext final : CypherClauseContextBase {
    MatchClauseContext() : CypherClauseContextBase(CypherClauseKind::kMatch) {}

    std::vector&amp;lt;NodeInfo&amp;gt;                       nodeInfos; // The vertices involved in the pattern
    std::vector&amp;lt;EdgeInfo&amp;gt;                       edgeInfos; // The edges involved in the pattern
    PathBuildExpression*                        pathBuild{nullptr}; // Constructing the expression of Path
    std::unique_ptr&amp;lt;WhereClauseContext&amp;gt;         where; // filter SubClause
    std::unordered_map&amp;lt;std::string, AliasType&amp;gt;* aliasesUsed{nullptr}; // The specified alias
    std::unordered_map&amp;lt;std::string, AliasType&amp;gt;  aliasesGenerated; // The generated alias
};
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then, these steps are followed:&lt;/p&gt;

&lt;p&gt;1.Finding Planner for the Statement&lt;br&gt;
This is a MATCH statement, so MatchPlanner is found from the PlannerMap.&lt;/p&gt;

&lt;p&gt;2.Generating a Plan&lt;br&gt;
MatchPlanner::transform is called to generate an execution plan.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;StatusOr&amp;lt;SubPlan&amp;gt; MatchPlanner::transform(AstContext* astCtx) {
    if (astCtx-&amp;gt;sentence-&amp;gt;kind() != Sentence::Kind::kMatch) {
        return Status::Error("Only MATCH is accepted for match planner.");
    }
    auto* matchCtx = static_cast&amp;lt;MatchAstContext*&amp;gt;(astCtx);

    std::vector&amp;lt;SubPlan&amp;gt; subplans;
    for (auto&amp;amp; clauseCtx : matchCtx-&amp;gt;clauses) {
        switch (clauseCtx-&amp;gt;kind) {
            case CypherClauseKind::kMatch: {
                auto subplan = std::make_unique&amp;lt;MatchClausePlanner&amp;gt;()-&amp;gt;transform(clauseCtx.get());
                NG_RETURN_IF_ERROR(subplan);
                subplans.emplace_back(std::move(subplan).value());
                break;
            }
            case CypherClauseKind::kUnwind: {
                auto subplan = std::make_unique&amp;lt;UnwindClausePlanner&amp;gt;()-&amp;gt;transform(clauseCtx.get());
                NG_RETURN_IF_ERROR(subplan);
                auto&amp;amp; unwind = subplan.value().root;
                std::vector&amp;lt;std::string&amp;gt; inputCols;
                if (!subplans.empty()) {
                    auto input = subplans.back().root;
                    auto cols = input-&amp;gt;colNames();
                    for (auto col : cols) {
                        inputCols.emplace_back(col);
                    }
                }
                inputCols.emplace_back(unwind-&amp;gt;colNames().front());
                unwind-&amp;gt;setColNames(inputCols);
                subplans.emplace_back(std::move(subplan).value());
                break;
            }
            case CypherClauseKind::kWith: {
                auto subplan = std::make_unique&amp;lt;WithClausePlanner&amp;gt;()-&amp;gt;transform(clauseCtx.get());
                NG_RETURN_IF_ERROR(subplan);
                subplans.emplace_back(std::move(subplan).value());
                break;
            }
            case CypherClauseKind::kReturn: {
                auto subplan = std::make_unique&amp;lt;ReturnClausePlanner&amp;gt;()-&amp;gt;transform(clauseCtx.get());
                NG_RETURN_IF_ERROR(subplan);
                subplans.emplace_back(std::move(subplan).value());
                break;
            }
            default: { return Status::Error("Unsupported clause."); }
        }
    }

    auto finalPlan = connectSegments(astCtx, subplans, matchCtx-&amp;gt;clauses);
    NG_RETURN_IF_ERROR(finalPlan);
    return std::move(finalPlan).value();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A MATCH statement may be composed of multiple MATCH, UNWIND, WITH, and RETURNclauses. Therefore, with MatchPlanner::transform, the corresponding ClausePlanners are called directly to generate the corresponding SubPlans, and then the SubPlans are connected end to end by SegmentsConnector according to the appropriate connection strategies.&lt;/p&gt;

&lt;p&gt;In the example statement, the first clause is a MATCH clause: MATCH (v:player)-[:like*2..4]-(v2:player), so MatchClausePlanner::transform is called.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;StatusOr&amp;lt;SubPlan&amp;gt; MatchClausePlanner::transform(CypherClauseContextBase* clauseCtx) {
    if (clauseCtx-&amp;gt;kind != CypherClauseKind::kMatch) {
        return Status::Error("Not a valid context for MatchClausePlanner.");
    }

    auto* matchClauseCtx = static_cast&amp;lt;MatchClauseContext*&amp;gt;(clauseCtx);
    auto&amp;amp; nodeInfos = matchClauseCtx-&amp;gt;nodeInfos;
    auto&amp;amp; edgeInfos = matchClauseCtx-&amp;gt;edgeInfos;
    SubPlan matchClausePlan;
    size_t startIndex = 0;
    bool startFromEdge = false;

    NG_RETURN_IF_ERROR(findStarts(matchClauseCtx, startFromEdge, startIndex, matchClausePlan));
    NG_RETURN_IF_ERROR(
        expand(nodeInfos, edgeInfos, matchClauseCtx, startFromEdge, startIndex, matchClausePlan));
    NG_RETURN_IF_ERROR(projectColumnsBySymbols(matchClauseCtx, startIndex, matchClausePlan));
    NG_RETURN_IF_ERROR(appendFilterPlan(matchClauseCtx, matchClausePlan));
    return matchClausePlan;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MatchClausePlanner::transform method performs these steps:&lt;/p&gt;

&lt;p&gt;Finding the starting vertex of the expansion.&lt;br&gt;
Currently, three strategies are available for finding the starting vertex. They are registered in startVidFinders.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// MATCH(n) WHERE id(n) = value RETURN n
startVidFinders.emplace_back(&amp;amp;VertexIdSeek::make);

// MATCH(n:Tag{prop:value}) RETURN n
// MATCH(n:Tag) WHERE n.prop = value RETURN n
startVidFinders.emplace_back(&amp;amp;PropIndexSeek::make);

// seek by tag or edge(index)
// MATCH(n: tag) RETURN n
// MATCH(s)-[:edge]-&amp;gt;(e) RETURN e
startVidFinders.emplace_back(&amp;amp;LabelIndexSeek::make);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of these three strategies, VertexIdSeek is the best, which can locate the specific VID of the starting vertex. PropIndexSeek is the second, which is converted to an IndexScan that filters vertices by the property. LabelIndexSeek will be converted to an IndexScan.&lt;/p&gt;

&lt;p&gt;For each strategy of finding the starting vertex, the findStarts function will traverse all the nodes in the MATCH pattern until it finds a node that can be used as the node of the starting vertex, and generates corresponding PlanNodes for finding the starting vertex.&lt;/p&gt;

&lt;p&gt;For this example statement, LabelIndexScan is used and the starting vertex is v. Finally, an IndexScan node is generated and the indexes on the player tag are used.&lt;/p&gt;

&lt;p&gt;According to the starting vertex and the MATCH pattern, an expansion across multiple steps is executed.&lt;br&gt;
For the example statement, the MATCH pattern is (v:player)-[:like*1..2]-(v2:player). It means v is the starting vertex, and an expansion across one or two steps along the like edge is executed, and the end vertex is of the player tag.&lt;/p&gt;

&lt;p&gt;Here is how the expansion is executed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Status Expand::doExpand(const NodeInfo&amp;amp; node, const EdgeInfo&amp;amp; edge, SubPlan* plan) {
    NG_RETURN_IF_ERROR(expandSteps(node, edge, plan));
    NG_RETURN_IF_ERROR(filterDatasetByPathLength(edge, plan-&amp;gt;root, plan));
    return Status::OK();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An expansion across multiple steps will generate a Loop node. The body of the Loop node is expandStep , which means a one-step expansion is executed from the given starting vertex and such an expansion generates a GetNeighbors node. The end vertex of each expansion is the starting vertex of the next expansion. It keeps looping until the maximum number of steps specified in the pattern is reached.&lt;/p&gt;

&lt;p&gt;To do the Step M expansion, the end vertex of the M-1 steps long path is used as the starting vertex of the expansion. By expanding one step more, the expansion result is constructed as a 1-step long path consisting of the source vertex of an edge and the edge itself. And then InnerJoin is performed to the 1-step long path and the previous M-1 steps long path to obtain a set of paths of M steps long.&lt;/p&gt;

&lt;p&gt;This set of paths are filtered to remove the paths with duplicate edges, which are not allowed for path expansion in openCypher. Finally, the end vertex is used as the starting vertex of the next expansion. Such expansions continue until the specified maximum number of steps is reached.&lt;/p&gt;

&lt;p&gt;After Loop, a UnionAllVersionVar node is generated. It combines the paths varying from 1-step to M-steps long that are generated from the execution of each loop of the body. The filterDatasetByPathLength() function will generate a Filter node to filter out all the paths that are shorter than the minimum number of steps specified in the MATCH pattern.&lt;/p&gt;

&lt;p&gt;After the expansion, the path looks like (v)-like-()-e-(v)-?, where the properties of the end vertex is still missing. At this point, generating a GetVertices node is needed. When the end vertex is obtained, an InnerJoin is performed to it and the M-steps long path, and then we will have a set of paths that meet the requirements of the MATCH pattern.&lt;/p&gt;

&lt;p&gt;More information about the expansion across multiple steps of MATCH will be introduced in a new article “Variable Length Pattern Match”.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Build Start node from first step
SubPlan loopBodyPlan;
PlanNode* startNode = StartNode::make(matchCtx_-&amp;gt;qctx);
startNode-&amp;gt;setOutputVar(firstStep-&amp;gt;outputVar());
startNode-&amp;gt;setColNames(firstStep-&amp;gt;colNames());
loopBodyPlan.tail = startNode;
loopBodyPlan.root = startNode;

// Construct loop body
NG_RETURN_IF_ERROR(expandStep(edge,
                              startNode,                // dep
                              startNode-&amp;gt;outputVar(),   // inputVar
                              nullptr,
                              &amp;amp;loopBodyPlan));

NG_RETURN_IF_ERROR(collectData(startNode,           // left join node
                               loopBodyPlan.root,   // right join node
                               &amp;amp;firstStep,          // passThrough
                               &amp;amp;subplan));
// Union node
auto body = subplan.root;

// Loop condition
auto condition = buildExpandCondition(body-&amp;gt;outputVar(), startIndex, maxHop);

// Create loop
auto* loop = Loop::make(matchCtx_-&amp;gt;qctx, firstStep, body, condition);

// Unionize the results of each expansion which are stored in the firstStep node
auto uResNode = UnionAllVersionVar::make(matchCtx_-&amp;gt;qctx, loop);
uResNode-&amp;gt;setInputVar(firstStep-&amp;gt;outputVar());
uResNode-&amp;gt;setColNames({kPathStr});

subplan.root = uResNode;
plan-&amp;gt;root = subplan.root; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A table is output and its column names are determined.&lt;br&gt;
All named symbols specified in the MATCH pattern are used as the column names to generate a table for the subsequent clauses, which will generate a Project node.&lt;/p&gt;

&lt;p&gt;The second clause in the example statement is WITH. It calls WithClause::transform to generate SubPlans.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WITH v, v2.age AS age ORDER BY age WHERE age &amp;gt; 18
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This WITH clause yields a table with two columns named v and v2.age. These columns are sorted by age, and then the table is used as a filter.&lt;/p&gt;

&lt;p&gt;The YIELD part will generate a Project node. The ORDER BY part will generate a Sort node. And the WHERE part will generate a Filter node.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F136780267-3e72afd6-5628-48cf-b548-3fae04724079.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F136780267-3e72afd6-5628-48cf-b548-3fae04724079.png" alt="Nebula Graph Source Code Explained: Planner"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The third clause is RETURN. It will generate a Project node.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RETURN id(v), age
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The complete execution plan of the example statement is shown as follows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F136779429-419ce787-a28b-4669-b3e0-8ea675f5d64e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F136779429-419ce787-a28b-4669-b3e0-8ea675f5d64e.png" alt="Nebula Graph Source Code Explained: Planner"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the end of this article.&lt;/p&gt;

&lt;p&gt;If you encounter any problems in the process of using Nebula Graph, please refer to &lt;a&gt;Nebula Graph Database Manual&lt;/a&gt; to troubleshoot the problem. It records in detail the knowledge points and specific usage of the graph database and the graph database Nebula Graph.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>database</category>
      <category>devops</category>
      <category>programming</category>
    </item>
    <item>
      <title>1 simple way to implement variable-Length Pattern Matching</title>
      <dc:creator>lisahui</dc:creator>
      <pubDate>Fri, 22 Oct 2021 06:06:50 +0000</pubDate>
      <link>https://dev.to/lisahui/nebula-graph-how-variable-length-pattern-matching-is-implemented-1pb5</link>
      <guid>https://dev.to/lisahui/nebula-graph-how-variable-length-pattern-matching-is-implemented-1pb5</guid>
      <description>&lt;p&gt;At the very heart of openCypher, the MATCH clause allows you to specify simple query patterns to retrieve the relationships from a graph database. A variable-length pattern is commonly used to describe paths and it is Nebula Graph’s first try to get nGQL compatible with openCypher in the MATCH clause.&lt;/p&gt;

&lt;p&gt;As can be seen from the previous articles of this series, an execution plan is composed of physical operators. Each operator is responsible for executing unique computational logics. To implement the MATCH clause, the operators such as GetNeighbors, GetVertices, Join, Project, Filter, and Loop, which have been introduced in the previous articles, are needed. Unlike the tree structure in a relational database, the execution process expressed by an execution plan in Nebula Graph is a cyclic graph. How to transform a variable-length pattern into a physical plan in Nebula Graph is the focus of the Planner. In this article, we will introduce how variable-length pattern matching is implemented in Nebula Graph.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem Analysis
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Fixed-Length Pattern
&lt;/h3&gt;

&lt;p&gt;In a MATCH clause, a fixed-length pattern is commonly used to search for a relationship. If a fixed-length pattern is considered a special case of the variable-length pattern, that is, a pattern describing a path of a specified length, the implementations of both can be unified. Here are the examples.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Fixed-length pattern MATCH (v)-[e]-(v2)
// Variable-length pattern MATCH (v)-[e*1..1]-(v2)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The preceding examples differ from each other in the type of the e variable. In the fixed-length pattern, e represents an edge, while in the variable-length one, e represents a list of edges of length 1.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connecting Variable-Length Patterns
&lt;/h3&gt;

&lt;p&gt;According to the syntax of openCypher, a MATCH clause allows you to specify a combination of various patterns for describing complicated paths. For example, two variable-length patterns can be connected as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MATCH (v)-[e*1..3]-(v2)-[ee*2..4]-(v3)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pattern combination in the preceding example is extendable, which means by connecting variable-length and fixed-length patterns in different ways, various complicated paths can be queried. Therefore, we must find a pattern to generate an execution plan to iterate the whole process recursively. The following conditions must be considered:&lt;/p&gt;

&lt;p&gt;The following variable-length path depends on the preceding one.&lt;br&gt;
The variables in the following pattern depend on the preceding pattern.&lt;br&gt;
Before the next traversal step, the starting vertex must be de-duplicated.&lt;br&gt;
From the following example, you can see that as long as an execution plan can be generated for the part of ()-[:like*m..n]-, combinations and iterations may be applied to generate plans for the subsequent parts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;()-[:like*m..n]- ()-[:like*k..l]- ()
 \____________/   \____________/   \_/
    Pattern1         Pattern2       Pattern3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Execution Plan
&lt;/h2&gt;

&lt;p&gt;In this section, we will introduce how the ()-[:like*m..n]- part in the preceding example is transformed into a physical execution plan in Nebula Graph. This pattern describes a graph of a minimum of m hops and a maximum of n hops. In Nebula Graph, a one-step traversal is completed by the GetNeighbors operator. To implement a multi-step traversal, each traversal step must call the GetNeighbors operator again on the basis of the previous step, and when the traversal of all the steps are completed, all the retrieved vertices and edges are connected end to end to form a single path. What users need is the paths of m to n relationships. However, in the execution process, paths of length 1 to length n are queried and are stored for output or for the next traversal, but only the paths of length m to n are retrieved.&lt;/p&gt;

&lt;h3&gt;
  
  
  One-Step Traversal
&lt;/h3&gt;

&lt;p&gt;Let’s see what the one-step traversal looks like. In Nebula Graph, the source vertex is stored together with its outgoing edges, so retrieving them does not need to access data across partitions. However, the destination vertex and its incoming edges are stored in different partitions, so GetVertices is necessary for retrieving the properties of the vertex. In addition, to avoid replicated scanning of Storage, the source vertices must be de-duplicated before the traversal. The execution plan of a one-step traversal is shown as follows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F138244211-8f1aaec3-b1f4-4e52-b74c-7d547b4f350f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F138244211-8f1aaec3-b1f4-4e52-b74c-7d547b4f350f.png" alt="One-Step Traversal"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-Step Traversal
&lt;/h3&gt;

&lt;p&gt;The process of a multi-step traversal is the repetition of one-step traversal. However, we can see that the GetNeighbors operator can retrieve the properties of an edge’s source vertex, so the GetVertices operator can be omitted in the previous step. Here is an execution plan of a two-step traversal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F138244372-74619d76-8b10-481a-9dde-d065d801f847.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F138244372-74619d76-8b10-481a-9dde-d065d801f847.png" alt="Multi-Step Traversal"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Storing Paths
&lt;/h3&gt;

&lt;p&gt;The paths retrieved in each traversal step may be needed at the end of the traversal, so all the paths must be stored. The paths for a two-step traversal are connected by the Join operator. In the result of the example ()-[e:like*m..n]-, e represents a list of data (edges), so Union is needed to merge the results of each traversal step. The execution plan will be evolved further as follows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F138245877-36d53b4a-0643-4058-8ffb-23502edc0901.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F138245877-36d53b4a-0643-4058-8ffb-23502edc0901.png" alt="One-Step Traversal"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Connecting Variable-Length Patterns
&lt;/h3&gt;

&lt;p&gt;After the implementations of the preceding process, a physical plan will be generated for the ()-[e:like*m..n]- pattern. If multiple similar patterns are connected together, such a process is iterated. However, before the iteration, the results of the previous process must be filtered to get the paths of length m to length n. The retrieved dataset of the previous process involves the paths of length 1 to length n, so filtering them by path length is needed. When the variable-length patterns are connected together, the execution plan becomes as follows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F138246601-acec9a6e-07b7-4f2d-ba0e-40850dabb63c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fuser-images.githubusercontent.com%2F90186547%2F138246601-acec9a6e-07b7-4f2d-ba0e-40850dabb63c.png" alt="One-Step Traversal"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the step-by-step decomposition of the patterns, the expected execution plan for the MATCH clause is finally generated. As you can see, it takes a lot of effort to transform a complicated pattern into the underlying interfaces for a traversal. Of course, the execution plan can be optimized, such as the multi-step traversal can be encapsulated by using the Loop operator and the sub-plan of a one-step traversal can be reused, which will not be detailed in this article. If you are interested, please refer to &lt;a href="https://github.com/vesoft-inc/nebula/blob/master/src/graph/planner/match/Expand.cpp" rel="noopener noreferrer"&gt;the source code of Nebula Graph&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This article demonstrated the process of generating an execution plan for a MATCH clause with a variable-length pattern. While reading the article, you may have this question: Why such a basic and simple path query will generate such a complicated execution plan in Nebula Graph? It’s not like Neo4j, where only a few operators are needed to complete the same job. In Nebula Graph, complicated directed acyclic graphs (DAG) are generated.&lt;/p&gt;

&lt;p&gt;The answer is that in Nebula Graph, the operators are closer to the underlying interfaces and there is a lack of semantic abstractions for higher-level graph operations. The operator granularity is too fine, so too many details need to be considered to implement the optimization of the upper layer. We will further study the execution operators to gradually improve the functionality and the performance of the MATCH clause.&lt;/p&gt;

&lt;p&gt;If you encounter any problems in the process of using Nebula Graph, please refer to &lt;a&gt;Nebula Graph Database Manual&lt;/a&gt; to troubleshoot the problem. It records in detail the knowledge points and specific usage of the graph database and the graph database Nebula Graph.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>opensource</category>
      <category>database</category>
      <category>devops</category>
    </item>
    <item>
      <title>2 Modules in Nebula Graph: Scheduler &amp; Executor</title>
      <dc:creator>lisahui</dc:creator>
      <pubDate>Mon, 11 Oct 2021 07:26:16 +0000</pubDate>
      <link>https://dev.to/lisahui/2-modules-in-nebula-graph-scheduler-executor-41g</link>
      <guid>https://dev.to/lisahui/2-modules-in-nebula-graph-scheduler-executor-41g</guid>
      <description>&lt;p&gt;You may have learned the optimizer of Nebula Graph’s query engine in the last article. In this article, we will introduce how the Scheduler and the Executor, the last two modules of the query engine, are implemented.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;In the execution phase, the execution engine uses the Scheduler to transform a physical execution plan, generated by the Planner, into a series of Executors to drive their execution. Each PlanNode in a physical execution plan has a corresponding Executor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structure of Source Files
&lt;/h2&gt;

&lt;p&gt;The source code of the Scheduler is under the src/scheduler directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/scheduler
├── AsyncMsgNotifyBasedScheduler.cpp
├── AsyncMsgNotifyBasedScheduler.h
├── CMakeLists.txt
├── Scheduler.cpp
└── Scheduler.h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Scheduler abstract class defines the common interfaces of the schedulers, which can inherit the features from the class to implement various types of schedulers. The AsyncMsgNotifyBasedScheduler scheduler has been implemented. By using the asynchronous message communication and breadth-first search algorithm, it can be prevented from stack overflow errors. The source code of the Executor is under the src/executor directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/executor
├── admin
├── algo
├── CMakeLists.txt
├── ExecutionError.h
├── Executor.cpp
├── Executor.h
├── logic
├── maintain
├── mutate
├── query
├── StorageAccessExecutor.cpp
├── StorageAccessExecutor.h
└── test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Process
&lt;/h2&gt;

&lt;p&gt;First, the Scheduler starts the traversal of the entire execution plan from its root node by using the breadth-first search algorithm and builds their notification mechanism according to the dependencies between nodes. During the execution phase, each node will be scheduled to be executed after being notified that all the nodes it depends on have been executed successfully. For a node, once executed, it will notify its dependent nodes until the entire plan is executed successfully.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void AsyncMsgNotifyBasedScheduler::runExecutor(
    std::vector&amp;lt;folly::Future&amp;lt;Status&amp;gt;&amp;gt;&amp;amp;&amp;amp; futures,
    Executor* exe,
    folly::Executor* runner,
    std::vector&amp;lt;folly::Promise&amp;lt;Status&amp;gt;&amp;gt;&amp;amp;&amp;amp; promises) const {
    folly::collect(futures).via(runner).thenTry(
        [exe, pros = std::move(promises), this](auto&amp;amp;&amp;amp; t) mutable {
            if (t.hasException()) {
                return notifyError(pros, Status::Error(t.exception().what()));
            }
            auto status = std::move(t).value();
            auto depStatus = checkStatus(std::move(status));
            if (!depStatus.ok()) {
                return notifyError(pros, depStatus);
            }
            // Execute in current thread.
            std::move(execute(exe)).thenTry(
                [pros = std::move(pros), this](auto&amp;amp;&amp;amp; exeTry) mutable {
                    if (exeTry.hasException()) {
                        return notifyError(pros, Status::Error(exeTry.exception().what()));
                    }
                    auto exeStatus = std::move(exeTry).value();
                    if (!exeStatus.ok()) {
                        return notifyError(pros, exeStatus);
                    }
                    return notifyOK(pros);
                });
        });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each Executor goes through four phases: “create”, “open”, “execute”, and then “close”.&lt;/p&gt;

&lt;h3&gt;
  
  
  create
&lt;/h3&gt;

&lt;p&gt;In the “create” phase, an appropriate Executor will be generated according to the node type.&lt;/p&gt;

&lt;h3&gt;
  
  
  open
&lt;/h3&gt;

&lt;p&gt;In the “open” phase, before the execution starts, the Executor is initialized, the slow queries are terminated, and the memory watermark is checked. When using Nebula Graph, you can use kill to terminate a query, so the status of the current execution plan must be checked before the execution of each Executor. If the plan is in the killed status, the execution will be terminated. Before the execution of each query Executor, it is necessary to check whether the amount of free memory has fallen below the watermark. If the watermark is reached, the execution will be terminated, which may avoid OOM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Status Executor::open() {
    if (qctx_-&amp;gt;isKilled()) {
        VLOG(1) &amp;lt;&amp;lt; "Execution is being killed. session: " &amp;lt;&amp;lt; qctx()-&amp;gt;rctx()-&amp;gt;session()-&amp;gt;id()
            &amp;lt;&amp;lt; "ep: " &amp;lt;&amp;lt; qctx()-&amp;gt;plan()-&amp;gt;id()
            &amp;lt;&amp;lt; "query: " &amp;lt;&amp;lt; qctx()-&amp;gt;rctx()-&amp;gt;query();
        return Status::Error("Execution had been killed");
    }
    auto status = MemInfo::make();
    NG_RETURN_IF_ERROR(status);
    auto mem = std::move(status).value();
    if (node_-&amp;gt;isQueryNode() &amp;amp;&amp;amp; mem-&amp;gt;hitsHighWatermark(FLAGS_system_memory_high_watermark_ratio)) {
        return Status::Error(
            "Used memory(%ldKB) hits the high watermark(%lf) of total system memory(%ldKB).",
            mem-&amp;gt;usedInKB(),
            FLAGS_system_memory_high_watermark_ratio,
            mem-&amp;gt;totalInKB());
    }
    numRows_ = 0;
    execTime_ = 0;
    totalDuration_.reset();
    return Status::OK();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  execute
&lt;/h3&gt;

&lt;p&gt;The input and output of a query Executor are in the form of tables (DataSet). The execution of an Executor is based on the iterator model, which means that for each calculation, the next() method of the iterator of the input table is called to retrieve a row of data and then the calculation is performed. Such a process is repeated until the traversal of the entire input table is done. The results of the calculations are constructed into a new table and output to the next Executor as its input.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;folly::Future&amp;lt;Status&amp;gt; ProjectExecutor::execute() {
    SCOPED_TIMER(&amp;amp;execTime_);
    auto* project = asNode&amp;lt;Project&amp;gt;(node());
    auto columns = project-&amp;gt;columns()-&amp;gt;columns();
    auto iter = ectx_-&amp;gt;getResult(project-&amp;gt;inputVar()).iter();
    DCHECK(!!iter);
    QueryExpressionContext ctx(ectx_);

    VLOG(1) &amp;lt;&amp;lt; "input: " &amp;lt;&amp;lt; project-&amp;gt;inputVar();
    DataSet ds;
    ds.colNames = project-&amp;gt;colNames();
    ds.rows.reserve(iter-&amp;gt;size());
    for (; iter-&amp;gt;valid(); iter-&amp;gt;next()) {
        Row row;
        for (auto&amp;amp; col : columns) {
            Value val = col-&amp;gt;expr()-&amp;gt;eval(ctx(iter.get()));
            row.values.emplace_back(std::move(val));
        }
        ds.rows.emplace_back(std::move(row));
    }
    VLOG(1) &amp;lt;&amp;lt; node()-&amp;gt;outputVar() &amp;lt;&amp;lt; ":" &amp;lt;&amp;lt; ds;
    return finish(ResultBuilder().value(Value(std::move(ds))).finish());
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the input table of the current Executor cannot be used by the other Executors as their input, the memory occupied by the table will be dropped in the execution phase to reduce memory usage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;void Executor::drop() {
    for (const auto &amp;amp;inputVar : node()-&amp;gt;inputVars()) {
        if (inputVar != nullptr) {
            // Make sure use the variable happened-before decrement count
            if (inputVar-&amp;gt;userCount.fetch_sub(1, std::memory_order_release) == 1) {
                // Make sure drop happened-after count decrement
                CHECK_EQ(inputVar-&amp;gt;userCount.load(std::memory_order_acquire), 0);
                ectx_-&amp;gt;dropResult(inputVar-&amp;gt;name);
                VLOG(1) &amp;lt;&amp;lt; "Drop variable " &amp;lt;&amp;lt; node()-&amp;gt;outputVar();
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  close
&lt;/h3&gt;

&lt;p&gt;After the execution of an Executor is done, some collected execution information, such as execution time and the number of rows in the output table, is added to the profiling statistics. You can run a PROFILE statement and then view the statistics in the returned result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Execution Plan (optimize time 141 us)

-----+------------------+--------------+-----------------------------------------------------+--------------------------------------
| id | name             | dependencies | profiling data                                      | operator info                       |
-----+------------------+--------------+-----------------------------------------------------+--------------------------------------
|  2 | Project          | 3            | ver: 0, rows: 56, execTime: 147us, totalTime: 160us | outputVar: [                        |
|    |                  |              |                                                     |   {                                 |
|    |                  |              |                                                     |     "colNames": [                   |
|    |                  |              |                                                     |       "VertexID",                   |
|    |                  |              |                                                     |       "player.age"                  |
|    |                  |              |                                                     |     ],                              |
|    |                  |              |                                                     |     "name": "__Project_2",          |
|    |                  |              |                                                     |     "type": "DATASET"               |
|    |                  |              |                                                     |   }                                 |
|    |                  |              |                                                     | ]                                   |
|    |                  |              |                                                     | inputVar: __TagIndexFullScan_1      |
|    |                  |              |                                                     | columns: [                          |
|    |                  |              |                                                     |   "$-.VertexID AS VertexID",        |
|    |                  |              |                                                     |   "player.age"                      |
|    |                  |              |                                                     | ]                                   |
----------+------------------+--------------+-----------------------------------------------------+--------------------------------------
|  3 | TagIndexFullScan | 0            | ver: 0, rows: 56, execTime: 0us, totalTime: 6863us  | outputVar: [                        |
|    |                  |              |                                                     |   {                                 |
|    |                  |              |                                                     |     "colNames": [                   |
|    |                  |              |                                                     |       "VertexID",                   |
|    |                  |              |                                                     |       "player.age"                  |
|    |                  |              |                                                     |     ],                              |
|    |                  |              |                                                     |     "name": "__TagIndexFullScan_1", |
|    |                  |              |                                                     |     "type": "DATASET"               |
|    |                  |              |                                                     |   }                                 |
|    |                  |              |                                                     | ]                                   |
|    |                  |              |                                                     | inputVar:                           |
|    |                  |              |                                                     | space: 318                          |
|    |                  |              |                                                     | dedup: false                        |
|    |                  |              |                                                     | limit: 9223372036854775807          |
|    |                  |              |                                                     | filter:                             |
|    |                  |              |                                                     | orderBy: []                         |
|    |                  |              |                                                     | schemaId: 319                       |
|    |                  |              |                                                     | isEdge: false                       |
|    |                  |              |                                                     | returnCols: [                       |
|    |                  |              |                                                     |   "_vid",                           |
|    |                  |              |                                                     |   "age"                             |
|    |                  |              |                                                     | ]                                   |
|    |                  |              |                                                     | indexCtx: [                         |
|    |                  |              |                                                     |   {                                 |
|    |                  |              |                                                     |     "columnHints": [],              |
|    |                  |              |                                                     |     "index_id": 325,                |
|    |                  |              |                                                     |     "filter": ""                    |
|    |                  |              |                                                     |   }                                 |
|    |                  |              |                                                     | ]                                   |
----------+------------------+--------------+-----------------------------------------------------+--------------------------------------
|  0 | Start            |              | ver: 0, rows: 0, execTime: 1us, totalTime: 19us     | outputVar: [                        |
|    |                  |              |                                                     |   {                                 |
|    |                  |              |                                                     |     "colNames": [],                 |
|    |                  |              |                                                     |     "type": "DATASET",              |
|    |                  |              |                                                     |     "name": "__Start_0"             |
|    |                  |              |                                                     |   }                                 |
|    |                  |              |                                                     | ]                                   |
----------+------------------+--------------+-----------------------------------------------------+--------------------------------------  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So far, the explanation of the query engine source code has been completed. Next time we will explain the implementation of some features of Nebula Graph.&lt;/p&gt;

&lt;p&gt;If you encounter any problems in the process of using Nebula Graph, please refer to &lt;a href="https://docs.nebula-graph.io/2.5.0/pdf/NebulaGraph-EN.pdf"&gt;Nebula Graph Database Manual&lt;/a&gt; to troubleshoot the problem. It records in detail the knowledge points and specific usage of the graph database and the graph database Nebula Graph.&lt;/p&gt;

</description>
      <category>database</category>
      <category>devops</category>
      <category>opensource</category>
      <category>programming</category>
    </item>
    <item>
      <title>Nebula Operator Kind, oneliner installer for Nebula K8s Operator Playground</title>
      <dc:creator>lisahui</dc:creator>
      <pubDate>Thu, 30 Sep 2021 08:54:38 +0000</pubDate>
      <link>https://dev.to/lisahui/nebula-operator-kind-oneliner-installer-for-nebula-k8s-operator-playground-27k8</link>
      <guid>https://dev.to/lisahui/nebula-operator-kind-oneliner-installer-for-nebula-k8s-operator-playground-27k8</guid>
      <description>&lt;p&gt;Nebula-Kind, an one-liner command to try K8s Operator based Nebula Graph Cluster on your machine, with the help of KIND (K8s in Docker)&lt;/p&gt;

&lt;h2&gt;
  
  
  Nebula-Operator-Kind
&lt;/h2&gt;

&lt;p&gt;As a Cloud Native Distributed Database, Nebula Graph comes with an open-source K8s Operator to enable boostrap and maintain Nebula Graph Cluster from a K8s CRD.&lt;/p&gt;

&lt;p&gt;Normally it takes you some time to setup all the dependencies and control plane resources of the Nebula Operator. If you are as lazy as I am, this Nebula-Operator-Kind is made for you to quick start and play with Nebula Graph in KIND.&lt;/p&gt;

&lt;p&gt;Nebula-Operator-Kind is the one-liner for setup everything for you including:&lt;/p&gt;

&lt;p&gt;Docker&lt;br&gt;
K8s(KIND)&lt;br&gt;
PV Provider&lt;br&gt;
Nebula-Operator&lt;br&gt;
Nebula-Console&lt;br&gt;
nodePort for accessing the Cluster&lt;br&gt;
Kubectl for playing with KIND and Nebula Operator&lt;/p&gt;
&lt;h2&gt;
  
  
  How To Use
&lt;/h2&gt;

&lt;p&gt;Install Nebula-Operator-Kind:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -sL nebula-kind.siwei.io/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see this after it’s done&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsiwei.io%2Fen%2Fnebula-operator-kind%2Finstall_success.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsiwei.io%2Fen%2Fnebula-operator-kind%2Finstall_success.webp" alt="alt text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can connect to the cluster via ~/.nebula-kind/bin/console as below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.nebula-kind/bin/console -u user -p password --address=127.0.0.1 --port=30000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  More
&lt;/h2&gt;

&lt;p&gt;It’s in GitHub with more information you may be intrested in ;-), please try and feedback there~ &lt;a href="https://github.com/wey-gu/nebula-operator-kind" rel="noopener noreferrer"&gt;https://github.com/wey-gu/nebula-operator-kind&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Install on KubeSphere all-in-on cluster：&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -sL nebula-kind.siwei.io/install-ks-1.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install on existing K8s cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -sL nebula-kind.siwei.io/install-on-k8s.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>database</category>
      <category>opensource</category>
      <category>devops</category>
      <category>sql</category>
    </item>
  </channel>
</rss>
