ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

DataLoader 结合 Google Datastore:基于 @google-cloud/datastore 的批量读取与缓存实战

DataLoader 结合 Google Datastore:基于 @google-cloud/datastore 的批量读取与缓存实战 后端缓存抽象【免费下载链接】dataloaderDataLoader is a generic utility to be used as part of your applications data fetching layer to provide a consistent API over various backends and reduce requests to those backends via batching and caching.项目地址https://gitcode.com/gh_mirrors/da/dataloader点击查看免费下载Google Datastore 是 Google Cloud 提供的 NoSQL 文档型数据库其原生支持对多个 key 的批量操作batch operations这与 DataLoader 的批量加载batching与记忆化缓存caching机制天然契合。本文以仓库中的 examples/GoogleDatastore.md 为骨架完整拆解如何使用google-cloud/datastore客户端与 DataLoader 构建一个 Datastore 数据加载器并深入源码剖析cacheKeyFn、批量函数约束、结果排序等底层原理读完你将能直接复制该模式接入自己的 Node.js 服务尤其是 GraphQL 服务。为什么 Google Datastore 适合与 DataLoader 搭配DataLoader 的核心价值在于应用代码中到处散落的按单个 key 取数据的调用会在同一个事件循环帧single frame of execution内被合并成一次批量请求并把结果按 key 一一对应返回从而大幅减少对后端的往返请求次数。Datastore 恰好提供了等价的批量能力datastore.get(keys)可以一次接收多个 key 并返回对应的实体列表。一个需要 N 次单点查询的业务逻辑用 DataLoader 包装后通常只触发 1 次批量 get这正是两者搭配的根基。类似地仓库中的 examples/Redis.md 利用了 Redis 的MGETexamples/SQL.md 利用了WHERE id IN (...)都遵循同一模式。前置准备与版本说明安装两个依赖npm install --save dataloader npm install --save google-cloud/datastore本文示例对应的客户端 API 为google-cloud/datastore1.3.x关联文档所引用的版本采用new Datastore()实例化、datastore.get(keys)批量读取、entity[datastore.KEY]取实体 key 的用法。较新版本客户端的 API 可能存在差异请以你所安装版本对应的官方文档为准。dataloader包要求运行环境支持全局 ES6Promise与MapNode.js 所有受支持版本均满足。核心示例完整的 Google Datastore DataLoader以下代码是 examples/GoogleDatastore.md 中的完整示例补上了DataLoader的引入const DataLoader require(dataloader); const Datastore require(google-cloud/datastore); const datastore new Datastore(); const datastoreLoader new DataLoader( async keys { const results await datastore.get(keys); // Sort resulting entities by the keys they were requested with. const entities results[0]; const entitiesByKey {}; entities.forEach(entity { entitiesByKey[JSON.stringify(entity[datastore.KEY])] entity; }); return keys.map(key entitiesByKey[JSON.stringify(key)] || null); }, { // Datastore complex keys need to be converted to a string for use as cache keys cacheKeyFn: key JSON.stringify(key), }, );逐行拆解批量加载函数async keys {...}接收 DataLoader 合并后的 key 数组调用datastore.get(keys)发起一次批量读取。results是一个数组其中results[0]为查询到的实体数组。建立序列化 key → 实体索引entitiesByKey以JSON.stringify(entity[datastore.KEY])为键建立索引。entity[datastore.KEY]是 Datastore 实体上携带 key 信息的特殊属性序列化后形如{path:[{kind:User,id:123}]}。按请求顺序对齐结果keys.map(key entitiesByKey[JSON.stringify(key)] || null)用请求的每个 key 去索引取值。这是 DataLoader 批量函数最关键的一步——批量函数返回的数组长度必须与 keys 数组一致且每个索引位置必须与对应 key 对齐详见下文批量函数的硬性约束。查询不到的 key 返回null而不是抛错或跳过。cacheKeyFn: key JSON.stringify(key)Datastore 的 key 是复杂对象同一实体的 key 在不同请求中可能是不同的对象实例直接作为缓存 key 会因对象引用不同而命中失败。通过JSON.stringify归一化为字符串保证同一实体的不同 key 对象能命中同一缓存条目。从源码看 DataLoader 的批量机制批量函数的硬性约束批量加载函数接收一个 key 数组必须返回一个 Promiseresolve 为一个值或Error数组。src/index.js的dispatchBatch中明确校验了两条约束src/index.js返回数组的长度必须等于 keys 数组的长度否则抛出TypeError提示 did not return a Promise of an Array of the same length as the Array of keys数组每个索引位置的值必须与同索引的 key 一一对应。这就是上面示例必须先建索引、再按 keys 顺序 map的原因——Datastore 返回的实体顺序并不保证与请求的 keys 顺序一致直接return results[0]会导致值错位甚至因长度不一致而触发 DataLoader 的校验错误。仓库中 examples/RethinkDB.md 专门展示了这种顺序不保证 缺失 key 不返回记录导致的踩坑案例其解法先indexResults建立 Map再keys.map归一化与 Datastore 示例的思路完全一致。load 的合并调度src/index.js中load()src/index.js会把每次调用产生的 Promise 与 key 推入当前 batchbatch.keys、batch.callbacks同一帧内的所有load()共享同一个 batch帧结束时由_batchScheduleFn默认enqueuePostPromiseJob见 src/index.js调度dispatchBatch一次性执行批量函数。测试 src/tests/dataloader.test.js 验证了多次 load 合并为一次批量调用的行为。cacheKeyFn 的深入原理为什么默认的缓存键不够用DataLoader 默认cacheKeyFn是恒等函数key key源码见 src/index.js缓存本质是一个 ES6Map按 SameValueZero 语义比较键。当 key 是 Datastore 的复杂对象时两个代表同一实体的 key 对象若引用不同就会被视为不同键既无法命中缓存还可能在批量函数中重复出现。因此 Datastore 场景必须提供cacheKeyFn将对象序列化为字符串。源码与测试的印证构造器通过getValidCacheKeyFn(options)解析该选项非函数会抛出TypeErrorsrc/tests/abuse.test.js 有对应断言cacheKeyFn的this上下文被绑定为 loader 实例src/tests/dataloader.test.js当缓存被禁用cache: false时cacheKeyFn不会被调用src/tests/dataloader.test.js因为缓存键只在走缓存路径时才有意义。一个易被忽略的细节cacheKeyFn只影响缓存的键不影响传给批量函数的 keys。也就是说即使你把对象 key 序列化成字符串做缓存键批量函数收到的仍然是原始 key 对象。所以示例中批量函数内部也要用JSON.stringify(key)去查entitiesByKey索引二者必须使用同一套序列化规则否则会缓存命中正常、结果映射错位。缓存、错误处理与缺失值的最佳实践缺失 key返回 null 还是 Error示例对查不到的 key 返回null这是 Datastore 批量读取的自然语义。如果你希望调用方感知这个 key 不存在也可以在批量函数中返回new Error(...)——DataLoader 会把单个值的Error也缓存起来避免反复加载同样的错误并在对应 Promise 上 rejectloadMany()则会把错误作为结果数组中的Error实例返回而不整体 reject源码见 src/index.js。每请求一个 loader勿跨用户共享缓存DataLoader 的缓存是内存中的 memoization 缓存只服务于单次请求内不重复加载同一数据不能替代 Redis、Memcache 等应用级共享缓存。不同用户权限不同若跨请求共享一个 loader可能造成数据串号。正确做法是每个请求创建新的 loader 实例examples/GoogleDatastore.md 的 loader 同样应遵循此约定。若确有需要清缓存可用clear(key)、clearAll()或prime(key, value)主动管理。限制单批大小datastore.get(keys)一次性传入的 key 数量过多可能超出服务端限制此时可用maxBatchSize选项默认Infinity把超大批次拆成多个小批次const datastoreLoader new DataLoader(batchFn, { cacheKeyFn: key JSON.stringify(key), maxBatchSize: 500, // 每批最多 500 个 key });从源码看src/index.jsgetCurrentBatch会在当前 batch 的 keys 数量达到_maxBatchSize时立即开启新 batch从而把一次大请求切分为多次批量调用。仓库中 src/tests/dataloader.test.js 对maxBatchSize的分批行为有完整测试。生产环境接入建议在请求入口创建 loader典型的 express 模式是createLoaders(authToken)返回一个包含各 loader 的对象随请求传递见 README.md 的 per-request 示例。与 GraphQL 集成GraphQL 字段解析器天然独立若无批处理机制一个嵌套查询可能产生大量数据库请求把字段的 resolve 改为userLoader.load(user.bestFriendID)即可将 13 次请求降为 4 次左右详见 README.md。保持批量函数纯正只负责按 keys 取回实体并严格对齐顺序把序列化规则JSON.stringify在cacheKeyFn与批量函数内保持一致其余业务逻辑放在 loader 之外。小结本文以 examples/GoogleDatastore.md 的完整示例为核心结合 src/index.js 的源码实现与测试用例讲清了三个关键点一是 Datastore 批量读取与 DataLoader 批处理的天然契合二是批量函数按 keys 顺序对齐结果的硬约束及其实现技巧三是复杂对象 key 必须通过cacheKeyFn序列化才能正确缓存。将这段模式套用到你的 Node.js 服务即可用极少的代码换来对 Datastore 请求数量的大幅削减。赞分享后端缓存抽象【免费下载链接】dataloaderDataLoader is a generic utility to be used as part of your applications data fetching layer to provide a consistent API over various backends and reduce requests to those backends via batching and caching.项目地址https://gitcode.com/gh_mirrors/da/dataloader点击查看免费下载相关推荐免费搭建B站动态推送QQ机器人5分钟实现UP主内容自动同步免费搭建B站动态推送QQ机器人5分钟实现UP主内容自动同步 还在手动刷新B站等待心爱UP主的更新吗 想让QQ群自动接收B站直播开播提醒和最新动态吗Ha即时通讯后端3 步上手的文献分析工具深夜赶稿救星把论文阅读效率翻一倍3 步上手的文献分析工具深夜赶稿救星把论文阅读效率翻一倍 凌晨一点你的选题还悬在半空。桌面上躺着 200 篇文献 PDF导师上周就催你交综述提纲而你连AI 技能AI 插件人工智能工作流自动化Ice菜单栏管理快速3步调出你顺手的macOS状态栏Ice菜单栏管理快速3步调出你顺手的macOS状态栏 顶栏图标挤到30个以后每次找目标都成了肌肉记忆。Ice是macOS菜单栏管理工具拖拽重排图标、分区管桌面应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表