Dataloader用于缓存和批处理数据库请求

时间:2017-02-06 17:35:51

标签: javascript node.js function

dataloader如何缓存和批量请求并存储在密钥中。

https://github.com/facebook/dataloader

这里我需要dataloader包并创建一个实例,但它如何工作aby Ideas .............................. .....请为数据库查询,提供db find table

的示例
    var DataLoader = require('dataloader')

    var userLoader = new DataLoader(keys => myBatchGetUsers(keys));

userLoader.load(1)
  .then(user => userLoader.load(user.invitedByID))
  .then(invitedBy => console.log(`User 1 was invited by ${invitedBy}`));

// Elsewhere in your application
userLoader.load(2)
  .then(user => userLoader.load(user.lastInvitedID))
  .then(lastInvited => console.log(`User 2 last invited ${lastInvited}`));

2 个答案:

答案 0 :(得分:2)

来自Facebook的DataLoader实用程序的工作原理是将输入请求与您必须提供的批处理功能相结合。它仅适用于使用Identifiers

的请求

分为三个阶段:

  1. 聚合阶段:Loader对象上的任何请求都会延迟到process.nextTick
  2. 批处理阶段:Loader只需使用所有请求的密钥组合调用您提供的myBatchGetUsers功能。
  3. 分裂阶段:结果然后“拆分”,以便输入请求获得所需的响应部分。
  4. 这就是为什么在您提供的示例中,您应该只有两个请求:

    • 一个用户1和2
    • 然后一个用于相关用户(invitedByID

    要使用mongodb实现此功能,您应该只定义myBatchGetUsers函数以适当地使用find方法:

    function myBatchGetUsers(keys) {
        // usersCollection is a promisified mongodb collection
        return usersCollection.find(
           {
              _id: { $in: keys }
           }
        )
    }
    

答案 1 :(得分:0)

我发现重新创建我使用的dataloader部分很有帮助,以了解实现它的一种可能方式。 (就我而言,我仅使用.load()函数)

因此,创建DataLoader构造函数的新实例将为您带来两件事:

  1. 标识符列表(开头为空)
  2. 使用此标识符列表查询数据库的功能(您提供此功能)。

构造函数可能看起来像这样:

function DataLoader (_batchLoadingFn) {
  this._keys = []
  this._batchLoadingFn = _batchLoadingFn
}

DataLoader构造函数的实例可以访问.load()函数,该函数必须能够访问_keys属性。因此它是在DataLoad.prototype对象上定义的:

DataLoader.prototype.load = function(key) {
   // this._keys references the array defined in the constructor function
}

通过DataLoader构造函数(new DataLoader(fn)创建新对象时,传递的fn需要从某处获取数据,以键数组作为参数,并返回一个可解析的Promise到与键的初始数组相对应的值数组。

例如,这是一个伪函数,它接收一个键数组,并将相同的数组传回,但值加倍:

const batchLoadingFn = keys => new Promise( resolve => resolve(keys.map(k => k * 2)) )
keys: [1,2,3]
vals: [2,4,6]

keys[0] corresponds to vals[0]
keys[1] corresponds to vals[1]
keys[2] corresponds to vals[2]

然后,每次调用.load(indentifier)函数时,都将一个键添加到_keys数组中,并在某个时刻调用batchLoadingFn并将其传递给{{1} }数组作为参数。

诀窍是... 如何多次调用_keys ,而.load(id)仅执行一次?这很酷,也是我探索该库工作原理的原因。

我发现可以通过指定在超时后执行batchLoadingFn来执行此操作,但是如果在超时间隔之前再次调用batchLoadingFn,则取消超时,这是一个新键已添加,并重新安排了对.load()的调用。在代码中实现这一点看起来像这样:

batchLoadingFn

本质上调用DataLoader.prototype.load = function(key) { clearTimeout(this._timer) this.timer = setTimeout(() => this.batchLoadingFn(), 0) } 会删除对.load()的挂起调用,然后在事件循环的后面安排对batchLoadingFn的新调用。这样可以保证如果短时间内多次调用batchLoadingFn.load()仅被调用一次。实际上,这与防弹跳非常相似。或者,至少在构建网站并且您想在batchLoadingFn事件上做某事时,它很有用,但您得到的事件远远超出您想要处理的事件。我认为这称为“反跳”。

但是调用mousemove还需要将键推入.load(key)数组,我们可以在_keys函数体中通过将.load参数推到{ {1}}(仅key)。但是,_keys函数的约定是,它返回与key参数解析的内容有关的单个值。在某个时候,this._keys.push(key)将被调用并获得结果(它必须返回与.load相对应的结果)。此外,还要求batchLoadingFn实际上返回该值的承诺。

我认为接下来的这一点特别聪明(值得看一下源代码)!

_keys库不是保留batchLoadingFn中的键列表,而是实际上保留了与对dataloader函数的引用相关联的键列表,该键列表在调用时会导致作为_keys结果的解析值。 resolve返回一个promise,当它的.load()函数被调用时,promise被解析。

因此,.load()数组实际上保留了resolve元组的列表。当您的_keys返回时,将使用一个值(希望通过索引号与[key, resolve]数组中的项相对应)调用batchLoadingFn函数。

因此resolve函数看起来像这样(就将_keys元组推入.load数组而言):

[key, resolve]

剩下的就是用_keys键作为参数执行DataLoader.prototype.load = function(key) { const promisedValue = new Promise ( resolve => this._keys.push({key, resolve}) ) ... return promisedValue } ,并在返回时调用正确的batchLoadingFn函数

_keys

结合起来,所有实现上述功能的代码都在这里:

resolve

如果我没记错的话,我不认为this._batchLoadingFn(this._keys.map(k => k.key)) .then(values => { this._keys.forEach(({resolve}, i) => { resolve(values[i]) }) this._keys = [] // Reset for the next batch }) 库使用function DataLoader (_batchLoadingFn) { this._keys = [] this._batchLoadingFn = _batchLoadingFn } DataLoader.prototype.load = function(key) { clearTimeout(this._timer) const promisedValue = new Promise ( resolve => this._keys.push({key, resolve}) ) this._timer = setTimeout(() => { console.log('You should only see me printed once!') this._batchLoadingFn(this._keys.map(k => k.key)) .then(values => { this._keys.forEach(({resolve}, i) => { resolve(values[i]) }) this._keys = [] }) }, 0) return promisedValue } // Define a batch loading function const batchLoadingFunction = keys => new Promise( resolve => resolve(keys.map(k => k * 2)) ) // Create a new DataLoader const loader = new DataLoader(batchLoadingFunction) // call .load() twice in quick succession loader.load(1).then(result => console.log('Result with key = 1', result)) loader.load(2).then(result => console.log('Result with key = 2', result)) ,而是使用dataloader。但是我无法解决这个问题。