为什么使用数据加载器进行批处理在测试中不起作用?
Why does batching with a dataloader not work in a test?
问题
我正在尝试测试以下查询的性能:
query {
classes {
teachers {
user_id
}
}
}
当我启动我的服务器并运行通过 graphQL 游乐场进行查询时,数据加载器按预期工作并批处理查询:teachersForClasses
仅 运行 一次。
当我 运行 测试以对查询的性能进行基准测试时,teachersForClasses
运行 对 getClasses
返回的每个唯一 class 执行一次 getClasses
。
为什么行为不同?在这两种情况下,HTTP 请求都被发送到已经 运行ning 的服务器。
背景
我在使用 Apollo Server 创建 graphQL API 的项目中使用 node.js 的数据加载器库。
代码
function getClasses(args, ctx) {
/* returns a list of Class objects */
}
const resolvers = {
Class: {
teachers: (parent: Class, _args, ctx: Context) =>
ctx.loaders.class.teachers.load(parent.class_id)
}
Query: {
class: (_parent, args, ctx): Promise<Class[]> => getClasses(args, ctx)
}
}
加载器定义如下:
function teachersForClasses(classIds: readonly string[]) {
console.log(classIds) // added for debugging
/* returns an array of User objects for each class ID */
}
export const loader = {
class: {
teachers: new Dataloader<string, User[]>>(teachersForClasses)
}
}
测试
在此 运行 之前,服务器已经 运行 连接到 http://localhost:8080。
const url = 'http://localhost:8080'
const request = supertest(url)
async function runQuery(token: string) {
return request
.post('/user')
.set({
ContentType: 'application/json',
Authorization: token
})
.send({
query: `
{
classes {
teachers {
user_id
}
}
}`
})
}
describe('benchmarks', () => {
it('getTeachersForClasses', () => {
/*populates the database with data*/
...
for (let i=0; i < 10; i++) {
console.time('query')
const start = Date.now()
await runQuery(userToken)
const end = Date.now()
console.timeEnd('query')
}
})
})
我发现了问题:'teachers' 有一个异步指令(权限检查),这意味着每次使用 .load()
调用数据加载器时,它都是不同事件循环的一部分 'tick' (explained here) & 因此调用没有按预期进行批处理。
这在 graphQL 游乐场中不是问题,因为我正在与管理员用户进行调用,所以该指令立即得到解决。
问题
我正在尝试测试以下查询的性能:
query {
classes {
teachers {
user_id
}
}
}
当我启动我的服务器并运行通过 graphQL 游乐场进行查询时,数据加载器按预期工作并批处理查询:teachersForClasses
仅 运行 一次。
当我 运行 测试以对查询的性能进行基准测试时,teachersForClasses
运行 对 getClasses
返回的每个唯一 class 执行一次 getClasses
。
为什么行为不同?在这两种情况下,HTTP 请求都被发送到已经 运行ning 的服务器。
背景
我在使用 Apollo Server 创建 graphQL API 的项目中使用 node.js 的数据加载器库。
代码
function getClasses(args, ctx) {
/* returns a list of Class objects */
}
const resolvers = {
Class: {
teachers: (parent: Class, _args, ctx: Context) =>
ctx.loaders.class.teachers.load(parent.class_id)
}
Query: {
class: (_parent, args, ctx): Promise<Class[]> => getClasses(args, ctx)
}
}
加载器定义如下:
function teachersForClasses(classIds: readonly string[]) {
console.log(classIds) // added for debugging
/* returns an array of User objects for each class ID */
}
export const loader = {
class: {
teachers: new Dataloader<string, User[]>>(teachersForClasses)
}
}
测试
在此 运行 之前,服务器已经 运行 连接到 http://localhost:8080。
const url = 'http://localhost:8080'
const request = supertest(url)
async function runQuery(token: string) {
return request
.post('/user')
.set({
ContentType: 'application/json',
Authorization: token
})
.send({
query: `
{
classes {
teachers {
user_id
}
}
}`
})
}
describe('benchmarks', () => {
it('getTeachersForClasses', () => {
/*populates the database with data*/
...
for (let i=0; i < 10; i++) {
console.time('query')
const start = Date.now()
await runQuery(userToken)
const end = Date.now()
console.timeEnd('query')
}
})
})
我发现了问题:'teachers' 有一个异步指令(权限检查),这意味着每次使用 .load()
调用数据加载器时,它都是不同事件循环的一部分 'tick' (explained here) & 因此调用没有按预期进行批处理。
这在 graphQL 游乐场中不是问题,因为我正在与管理员用户进行调用,所以该指令立即得到解决。