慣性聚合 高效追讀感興趣之博客、新聞、科技資訊
閱原文 以慣性聚合開啟

推薦訂閱源

博客园 - 司徒正美
V
V2EX
T
Tailwind CSS Blog
有赞技术团队
有赞技术团队
aimingoo的专栏
aimingoo的专栏
Apple Machine Learning Research
Apple Machine Learning Research
IT之家
IT之家
Blog — PlanetScale
Blog — PlanetScale
A
About on SuperTechFans
月光博客
月光博客
T
The Blog of Author Tim Ferriss
宝玉的分享
宝玉的分享
Martin Fowler
Martin Fowler
博客园 - 聂微东
The GitHub Blog
The GitHub Blog
V
Visual Studio Blog
WordPress大学
WordPress大学
酷 壳 – CoolShell
酷 壳 – CoolShell
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python)
共享 GraphQL 片段如何悄无声息地扼杀了我们的列表性能
Ryo Tsugawa · 2026-05-24 · via DEV Community

某事觉迟

吾等之产品积压清单页滞涩。非至于破败,惟…偏。若切换自新启之示范租户至真实租户,具数据者,其迟滞乃可察。

乃量之:

  • 八项 二百零六毫秒,初字节至时
  • 六十七项 六百七十五毫秒,初字节至时

是乃相差469毫秒,或约每项线性衰减8毫秒。每增一项待处理事项,响应时间即增约8毫秒。此非佳兆。

初念欲归咎于前端——或组件树重绘过于激烈。然TTFB立时否之。浏览器未启渲染;服务器应答已耗时如此。

故瓶颈所在,乃介于API服务器与数据库之间。当穷究其源。

吾辈预载之术若何?吾辈行Go + gqlgen + 于Cloud Run,与Cloud SQL(PostgreSQL)相接。GraphQL之层,依简明之理而设:

客唯求所需之字段,主唯预所请之数据。

实则,吾之解析器检视GraphQL之选集,译为GORM之调用。Preload()若前端索求tasks { assignees { user } },后端必恭谨预载Tasks → Assignees → User。若仅索tasks { backlogStatus },吾等亦仅预Tasks → BacklogStatus

此乃"当"之设计。后端信 frontend 自能详述其数据所需,遂应之,恰如其分,不多不少。

然则,此乃理之所在。

实际情状

吾等列表页所发 GraphQL 查询(简之如下):

query GetProductBacklogItems($projectId: ID!) {
  productBacklogItems(projectId: $projectId) {
    edges {
      node {
        id
        name
        storyPoint
        priority
        progress
        backlogStatus { id name status }
        tasks {
          ...SubTaskFields
        }
      }
    }
  }
}

全屏模式 退出全屏模式

SubTaskFields若此:

fragment SubTaskFields on Task {
  id
  name
  backlogStatus { id name status }
  assignees {
    user { id givenName familyName }
  }
}

全屏模式 退出全屏模式

见其弊乎?列表页索求tasks { ...SubTaskFields },而SubTaskFieldsassignees { user { ... } }

列表页不显任务指派者。页上无一构件得读。task.assignees然查询终求之。

于后端,此乃触发之四级预载链— 每一项待办事项执行一次:

等级 预载
Tasks
Tasks.BacklogStatus
Tasks.Assignees
Tasks.Assignees.User

物六十七件,乃六十七乘四,叠级数据库之索。每一索皆越网络而击Cloud SQL。不怪其线性也.

众人何以未察

此最令人扼腕者:Fragment之行,恰如其分——惟其境异耳.

吾辈于积压之物,有二观:

  1. 列表之观 — 显姓名、状态、故事点、进度。无任务级指派信息.
  2. 详视图 — 显诸般,包括各子任务指派者.

详视图诚需assignees { user }SubTaskFields内。此Fragment为详视图而作,彼处无误。

列视图方借之。同属一Fragment,境遇各异,性能迥然不同。

规模尚微,人莫之察。八项乘四预载,或增六十四毫秒。此乃微末。然一旦逾五十项,线性之耗痛彻显现。

尚有他因:本地开发之机过速。 开发之时,数据库或为本地,或置邻近之Docker容器中——网络往返几近于无。每预载之费,仅微秒耳,故纵有四层,亦若瞬息。至乎生产,每预载皆需自Cloud Run至Cloud SQL一跃。此跃之迟滞,乘以四层×N项之物,遂将无形之累化为实滞之阻。

其训若此: GraphQL之片段,乃惠于开发者,非定数据所需之约也。若共此片段于异视之界,其显隐殊异,则默然自择非所必需之预载。

其解

第一步:去其不必要之字段于列查询

显见之策——止询tasks { ...SubTaskFields }于列查询:

query GetProductBacklogItems($projectId: ID!) {
  productBacklogItems(projectId: $projectId) {
    edges {
      node {
        id
        name
        storyPoint
        priority
        progress
        backlogStatus { id name status }
        # tasks removed — list page doesn't render subtask details
      }
    }
  }
}

入全屏模式 出全屏模式

然有伏焉。

第二步:其progress田野犹待事数据

progress每项待办事项之百分比也计算于服务器侧 自其子任中析出。其要者,计已成之任与全任之比。为此,服务器须载 TasksTasks.BacklogStatus

若前端辍请 tasks,则后端止预载之,progress 默然返零于诸事。此弊甚于迟滞。

第三步:EnhanceFields 之式

吾辈已立此法度矣。田野注解助手此法可保所需预载皆备,无论前端所求为何。吾辈亦以此法治项目统计与团队统计之事。

其意简明:于将所请字段付予预载层之前,当察所需服务器端运算之字段在否。若不在,则注入之。

func enhanceFields(fields []string) []string {
    // If the client didn't ask for "tasks" at all,
    // inject the minimum needed for progress calculation.
    if !contains(fields, "tasks") {
        fields = append(fields, "tasks", "tasks.backlogStatus")
    }
    return fields
}

入全景模式 出全屏模式

在解析器中,此助手居于选择集解析与预载构建之间。

func (r *resolver) ListItems(ctx context.Context, ...) {
    fields := enhanceFields(getRequestedFields(ctx))
    // fields is now guaranteed to include "tasks" + "tasks.backlogStatus",
    // but NOT "tasks.assignees" or "tasks.assignees.user"
    items := r.repo.FindAll(ctx, filters, fields)
    // ...
}

入全景模式 出全屏模式

要旨者:今列表与详述之视,已具不对称预载之深浅者,详视犹得四级(前端所求也)。列视仅得二级——足矣算进度,不引属主之数据,故未尝示之。

其果

前端所求 tasks { backlogStatus, assignees { user } } (不请任务)
后端预载 任務 → 暫存狀態 → 指派者 → 用戶 任務 → 暫存狀態
預載深度 4 2
首屏加載時間 (67項) 675毫秒 135毫秒

5倍迅捷.~8毫秒/項的線性退化實際消失。

至妙之处:详视图毫发无伤。犹自请之。tasks { ...SubTaskFields },且其EnhanceFields助者无作,当hasTasks此言已确。详情页无任何行为之变。

吾欲异行

若干心得自排错而来

  • TTFB乃汝初诊之要。 若首屏加载时延与项目数量成线性关系,则弊在服务器。勿耗光阴于React渲染之剖析,直至API之疑已释。

  • 片段乃易用之巧,非数据契约之本。 每当共享片段于异构视域,各视域之性能遂隐生牵绊。当思SubTaskFieldsList 之设。SubTaskFieldsDetail非一共享之Fragment也。

  • "服务器唯载所求"者,必求之当,乃得效。 GraphQL之精准数据获取,系于查询作者之精准。服务器忠实地行其所命,弊在所命非当。

  • 线性衰减,隐于小数据集。 件物八则,隐于八毫之速。百则则若墙垣阻隔。若惟以种子之数试之,则永难察此类之弊。当以生产之巨量数据为纲,察其详。


是文所本,乃Lasimban 之实效之修。此乃为Scrum之众所建之任务管理SaaS也。 免费可用 — 无需信用卡。

试Lasimban之免费→lasimban.team

Lasimban - 专注Scrum之任务管理器

使Scrum之开发更直观,更愉悦。Lasimban乃专注Scrum之任务管理器,示团队以正道。

favicon lasimban.team


汝于 GraphQL 之设,曾遇相似之片段共享陷阱乎?余甚欲闻诸队如何处理列表与详情预载之分割。