Falcor - 未缓存的深层嵌套引用

Posted

技术标签:

【中文标题】Falcor - 未缓存的深层嵌套引用【英文标题】:Falcor - Deep nested references not cached 【发布时间】:2019-07-13 05:23:31 【问题描述】:

当我请求包含嵌套引用的路由时,我发现 Falcor 客户端出现问题。

这是一个例子:

考虑以下来自 Falcor 服务器在 model.get 调用上的 JsonGraph 响应


  "todos": 
    "0":  "$type": "ref", "value": ["todosById", "id_0"] ,
    "1":  "$type": "ref", "value": ["todosById", "id_1"] ,
    "length": 2
  ,
  "todosById": 
    "id_0": 
      "name": "get milk",
      "label":  "$type": "ref", "value": ["labelsById", "lbl_0"] ,
      "completed": false
    ,
    "id_1": 
      "name": "do the laundry",
      "label":  "$type": "ref", "value": ["labelsById", "lbl_1"] ,
      "completed": false
    
  ,
  "labelsById": 
    "lbl_0":  "name": "groceries" ,
    "lbl_1":  "name": "home" 
  

当我使用以下路径调用model.get 时,以上所有 jsonGraph 结果都应该在缓存中:

model.get(['todos', from: 0, to: 1, ['completed', 'label', 'name']])

但是,手动访问缓存,我​​可以看到todostodosById 在缓存中,但不是labelsById

我不确定,但看起来 labelsById 不在缓存中,因为它是二级引用?

我在这里遗漏了什么还是 Falcor 缓存的预期行为? 有什么办法可以强制labelsById 在缓存中,因此不会发出额外的数据源请求?

感谢任何帮助!

这个问题可以在这个小项目中重现: https://github.com/ardeois/falcor-nested-references-cache-issue

更新

感谢@james-conkling 的回答,可以通过执行以下model.get 来缓存json 图:

model.get(
  ['todos', from: 0, to: 1, ['completed', 'name']],
  ['todos', from: 0, to: 1, 'label', 'name']
);

但是,在服务器端 Falcor 路由器将调用 todos[integers:indices] 路由两次。这可能会对您的 Falcor 服务器的 API 或数据库调用产生影响。

【问题讨论】:

【参考方案1】:

在路径集['todos', from: 0, to: 1, ['completed', 'label', 'name']] 中,以completedname 键结尾的路径在一个原子处终止。但是以label 键结尾的路径在引用处终止。如果您想真正遵循该 ref,则必须将其作为第二条路径包含在内:

[
   ['todos', from: 0, to: 1, ['completed', 'name']],
   ['todos', from: 0, to: 1, 'label', 'name']
]

一般来说,所有路径都应该在一个原子上终止,而不是在一个引用上。我不确定在 ref 上终止的路径的预期行为是什么,或者即使它已明确定义(如 your other question 所述,行为已从 v0 更改为 v1)。

model.get(...paths) 调用可以采用多个 pathSet 数组,因此重写查询应该像

model.get(
  ['todos', from: 0, to: 1, ['completed', 'name']],
  ['todos', from: 0, to: 1, 'label', 'name']
);

编辑

如下面的 cmets 所述,由于路由器处理程序一次只能解析一个 pathSet,具有多个 pathSet 的 GET 请求可能会导致对上游支持服务/db 的多个请求。一些可能的解决方案:

使用单一路径

使用单个路径 ['todos', range, ['completed', 'name', 'label'], 'name'] 重写请求。从技术上讲,除了todos.n.label.name(确实存在)之外,此请求还要求提供todos.n.completed.nametodos.n.label.name(不存在)。

但是,如果您的路由器处理程序为比匹配路径短的路径返回 pathValues,则应将较短的 pathValues 合并到您的 jsonGraph 缓存中。例如。匹配todos.0.completed.name时返回 path: ['todos', 0, 'completed'], value: true ,匹配todos.0.label.name时返回 path: ['todos', 0, 'label', 'name'], value: 'First TODO'

这可能是最简单的方法,但意味着您的查询在语义上并不正确(您故意要求不存在的路径)。

路由器发出的批量上游请求

在您的路由器中,将上游请求批量发送到您的支持服务/数据库。这并不总是直截了当的。一种可能的方法是使用 facebook 的 data-loader,它是为解决与 GraphQL 路由器的等效问题而编写的,但不一定与 GraphQL 相关联。另一种方法可以使用自定义 reducer 函数来组合使用相同刻度发出的请求(例如 here)。

重写你的架构

这样需要同时请求的所有路径的长度都是一样的。不过,这并不总是可能的,所以:耸耸肩。

【讨论】:

感谢您的澄清!但是,在同一个model.get 中设置 2 个路径实际上会调用 1 个 falcor 服务器,但路由会被解析两次,对吗?一种用于访问completedname 属性,一种用于访问label.name。所以这可以生成(在 Falcor 服务器中)对同一端点或数据库的 2 次调用。 @CorentinArdeois 是的,使用两条路径意味着您的路由器处理程序将被调用两次,可能导致对上游支持服务/db 的两次调用。有很多方法可以解决这个问题,但总的来说,我认为这是 Falcor 面临的最大问题之一。我将更新我的答案以解释缓解这种情况的方法。

以上是关于Falcor - 未缓存的深层嵌套引用的主要内容,如果未能解决你的问题,请参考以下文章

具有来自外部 Api 的缓存数据的 Falcor 模型

Falcor:避免过时的客户端缓存

在配置文件引导优化后嵌套 for 循环更快,但缓存未命中率更高

API 设计:缓存“部分”嵌套对象

shared_ptrs 是不是由于引用计数器原子递增/递减而遇到缓存未命中?

用于缓存引用的 Linux perf 命令