目录
DGraph 源码阅读 (4) - Query 流程
这篇文章是学习 DGraph 的第四篇(也是最后一篇),主要记录一下 DGraph 的读操作逻辑。
相对于 DGraph 的写操作,读操作比较简单。其中要注意的就是在执行 Query 的时候,怎么将 Query Dispatch 到其他 Alpha 节点上,以及怎么保证 ACID。首先我们来看看整个 Query 的执行流程。
读操作详细流程
读操作的入口函数是 Query
,这个方法会被 Http 和 GRPC 服务调用。接下来是整个函数的详细流程。
- 解析请求,这一步主要是将 JSON 或者 GraphQL+- 格式的请求转化为 Gql。注意这个 Gql 中包含了 Query 和 Vars。关于 Vars 的详情,请查看 DGraph 的文档。它的主要作用是一个中间变量,方便你做类似 Join 的查询。
- 申请一个 Timestamp。注意这个 Timestamp 很重要,DGraph 用它来保证 Linearizable。
- 调用
Process
函数,这个函数又会调用ProcessQuery
函数,这个函数是 Query 的核心逻辑。 - 等上一步返回之后,会把结果中的 Subgraph 转成 JSON 返回。
ProcessQuery 逻辑
遍历
QueryRequest
中 Query 字段,调用ToSubGraph
来生成对应的 SubGraph。注意这个函数会递归的调用嵌套的边,生成子 SubGraph。给 SubGraph 设置 ReadTs (之前申请的 Timstamp),并添加到一个数组中。
循环遍历这个 SubGraph,调用
ProcessGraph
来执行可以执行的(所有依赖的 Var 都已经计算好)。不断重复这一步直到所有的 SubGraph 都执行完。ProcessGraph
的主要逻辑包括:调用
createTaskQuery
。这个方法就是生成一个对应的 Protobuf 格式的 Query。调用
ProcessTaskOverNetwork
来执行 query。和写操作一样,如果发现这个 Query 中包含的 Predicate 被自己所属的 Group 处理,则直接调用processTask
,否则发送一个网络请求。processTask
会实际读取 Badger 来获得需要的数据,具体代码见helpProcessTask
。
Source Function 的作用
Source Function 就是 Query 中最外层传入的 func 参数。DGraph 对于 Source Function 做了一些限制,比如不能用 and 等连接符。这是因为 Source Function 会直接转化为读 Badger 的 Query,而 Badger 作为一个 KV,查询能力比较有限。除了 Source Function 之外,其他的 Filter 都是做 Post Filtering,并不会减少读取的数据量。
Linearizable 保证
就像在最开始说的一样,DGraph 使用申请到的 Timestamp 来保证 Linearizability。具体的做法就是在 processTask
中,会阻塞等待,直到超过请求中指定的 ReadTs。这也就意味着,在发起 Query 之前的所有操作都已经 Commit 了。而在从 Badger 读取的过程中,会读取所有在 ReadTs 之前提交的事务造成的改动。
总结一下
读操作的代码相对来说比较简单,我个人觉得最重要的部分包括:
- 对于 Var 的处理
- 保证 Linearizable
使用了一个全局一致的 Timestamp 来保证线性一致性,我觉得比较好理解。但是我没想明白的是,用于有阻塞等待的存在,是不是相当于所有的请求都必须等待之前的请求完成?这会不会造成系统并发的瓶颈?