cal.diy(cal.com)中的服务端性能实践:用 React.cache() 做请求级去重

发布时间:2026/9/8 19:54:40

cal.diy(cal.com)中的服务端性能实践:用 React.cache() 做请求级去重 cal.diycal.com中的服务端性能实践用 React.cache() 做请求级去重【免费下载链接】cal.diyScheduling infrastructure for absolutely everyone.项目地址: https://gitcode.com/GitHub_Trending/ca/cal.diy在 Next.js / React Server Components 应用中同一个页面请求内往往会有多个组件分别发起相同的认证查询或数据库读取造成重复的 DB 访问。本文基于 cal.diy 仓库内置的 server-cache-react 规则文档Vercel 出品、MIT 许可的 React 性能规则库中的一条 MEDIUM 级规则讲解React.cache()的请求级去重机制、适用边界并结合 cal.com 源码中真实的unstable_cache封装实现说明“请求内去重”与“跨请求缓存”在工程上如何各取所需。一、规则出处Vercel React Best Practices 规则库中的位置cal.diy 仓库在 agents/skills/vercel-react-best-practices 中内置了一份由 Vercel Engineering 维护的 React / Next.js 性能优化规则集共 45 条规则、8 个分类按影响面排序见 SKILL.md 中的优先级表优先级分类影响前缀1消除瀑布流CRITICALasync-2打包体积优化CRITICALbundle-3服务端性能HIGHserver-4客户端数据获取MEDIUM-HIGHclient-5~8重渲染 / 渲染 / JS / 高级模式MEDIUM ~ LOWrerender-等server-cache-react属于第 3 类“Server-Side Performance服务端性能”规则文件自身元信息frontmatter标注为标题Per-Request Deduplication with React.cache()影响等级MEDIUMimpactDescription: deduplicates within request标签server, cache, react-cache, deduplication完整的规则汇编版见 AGENTS.md 的 3.4 节约 L715-L735与本规则文件内容一致它的姊妹规则 server-cache-lru.md 则解决“跨请求缓存”问题。两条规则合起来覆盖了服务端数据获取的两种去重/缓存场景。二、规则要解决的问题同一请求内的重复查询规则文档给出的核心结论是一句话用React.cache()做服务端请求级去重收益最大的是认证authentication和数据库查询database queries这类场景。典型痛点是一个 RSC 页面中页面组件、侧边栏、导航栏各自独立地调用“获取当前用户”逻辑若该逻辑内部要await auth()拿会话再查一次数据库那么同一个 HTTP 请求内就会打出多次完全相同的查询。React.cache()的作用是把这类函数在“单次请求的生命周期内”记忆化import { cache } from react export const getCurrentUser cache(async () { const session await auth() if (!session?.user?.id) return null return await db.user.findUnique({ where: { id: session.user.id } }) })如规则原文所述在单个请求内多次调用getCurrentUser()只会真正执行一次查询。第二个及之后的调用直接复用第一次的结果对异步函数而言React 缓存的是返回的 Promise同一请求内并发调用共享同一个 Promise从而把 N 次会话解析 N 次findUnique收敛为 1 次。几个值得注意的机制细节它是“记忆化”而不是“缓存”React.cache()不引入任何过期时间、容量上限或持久化记忆化作用域就是一次渲染/一次请求请求结束即失效。因此它不承担“加速跨请求访问”的职责——这正是需要 LRU 的原因见第四节。适合无参或纯参函数规则示例中的getCurrentUser不接收参数。记忆化以参数身份为键参数来自不可变来源如当前请求内的稳定值时效果最确定带复杂可变参数的函数需谨慎评估。对认证链路的意义auth()这类函数通常内部包含 token 校验、会话存储读取甚至数据库查询是页面中扇出最广的公共依赖天然适合包一层cache()。三、边界请求内去重 vs 跨请求缓存同属server-分类的 server-cache-lru.md 明确划出了React.cache()的边界React.cache()only works within one request. For data shared across sequential requests用户先点了按钮 A、又点了按钮 B两个连续端点需要同一份数据use an LRU cache.该规则给出的 LRU 实现范式影响等级 HIGHimpactDescription: caches across requestsimport { LRUCache } from lru-cache const cache new LRUCachestring, any({ max: 1000, ttl: 5 * 60 * 1000 // 5 minutes }) export async function getUser(id: string) { const cached cache.get(id) if (cached) return cached const user await db.user.findUnique({ where: { id } }) cache.set(id, user) return user } // Request 1: DB query, result cached // Request 2: cache hit, no DB query适用条件是“用户在数秒内的连续操作会命中多个端点、且这些端点需要同一份数据”。规则同时指出了部署形态对 LRU 有效性的影响在函数实例可被并发请求共享的托管运行时中进程内 LRU 天然跨请求生效而在传统 serverless 中每次调用相互隔离跨进程缓存则需要 Redis 一类的共享存储。由此可以整理出清晰的选型表维度React.cache()LRU进程内/共享存储作用域单次请求跨请求受 TTL / 容量约束过期机制无请求结束即失效TTL 容量上限典型场景页面内多组件重复取数、认证解析连续操作命中多端点的同一数据部署约束无实例隔离时需外置存储四、cal.com 源码中的对照实践unstable_cache 封装从源码结构看cal.diy 的 Web 应用Next.js并没有直接依赖React.cache()而是把“请求去重/缓存”建立在 Next.js 的unstable_cache之上并做了仓库级的统一封装。这条实践链可以作为规则落地方式的真实参照。4.1 仓库级封装packages/lib/unstable_cachepackages/lib/unstable_cache/unstable_cache.ts 对next/cache的unstable_cache做了一层包装核心是解决序列化问题unstable_cache要求函数返回值可 JSON 序列化/** * This implementation is adapted from https://github.com/vercel/next.js/issues/51613#issuecomment-1892644565. * It is a wrapper around unstable_cache that adds serialization and deserialization */ import { unstable_cache } from next/cache; import { parse, stringify } from superjson; export const cache T, P extends unknown[]( fn: (...params: P) PromiseT, keys: Parameterstypeof unstable_cache[1], opts: Parameterstypeof unstable_cache[2] ) { const wrap async (params: unknown[]): Promisestring { const result await fn(...(params as P)); return stringify(result); }; const cachedFn unstable_cache(wrap, keys, opts); return async (...params: P): PromiseT { const result await cachedFn(params); return parse(result); }; };可以看到封装的要点入参以数组形式收集unstable_cache的键机制要求参数可序列化且多个参数需打包成单一数组参数传入出参用superjson的stringify/parse往返从而支持Date等原生类型。仓库内还有 getInstallCountPerApp.ts 等使用该封装的业务缓存函数。4.2 业务示例团队计划校验的缓存与失效apps/web/app/cache/membership.ts 展示了完整的“缓存 标签失效”闭环use server; import { MembershipRepository } from calcom/features/membership/repositories/MembershipRepository; import { NEXTJS_CACHE_TTL } from calcom/lib/constants; import { revalidateTag, unstable_cache } from next/cache; const CACHE_TAGS { HAS_TEAM_PLAN: MembershipRepository.hasAnyAcceptedMembershipByUserId, } as const; export const getCachedHasTeamPlan unstable_cache( async (userId: number) { const hasTeamPlan await MembershipRepository.hasAnyAcceptedMembershipByUserId(userId); return { hasTeamPlan: !!hasTeamPlan }; }, [getCachedHasTeamPlan], { revalidate: NEXTJS_CACHE_TTL, tags: [CACHE_TAGS.HAS_TEAM_PLAN], } ); export const revalidateHasTeamPlan async () { revalidateTag(CACHE_TAGS.HAS_TEAM_PLAN, max); };配套的参数与取值均有仓库依据revalidate取自常量NEXTJS_CACHE_TTL在 packages/lib/constants.ts 中定义为3600秒即 1 小时。tags缓存标签使用“仓库名 方法名”命名如MembershipRepository.hasAnyAcceptedMembershipByUserId便于定位缓存归属。失效方式revalidateTag(tag, max)将缓存 TTL 直接推进到最大即立刻失效——“读多写少 变更时定向失效”的组合而不是无差别清缓存。4.3 与规则的关系从源码结构看cal.com 的选择与规则文档并不矛盾而是分层互补React.cache()规则server-cache-react解决的是同一请求内的重复调用是 React 运行时的记忆化unstable_cache 封装 revalidateTagcal.com 现状解决的是跨请求的重复数据获取缓存键含函数键与参数userId带 TTL 与定向失效。也就是说若一个页面在同一请求内多处需要hasAnyAcceptedMembershipByUserId的结果unstable_cache在请求内同样只查一次库第二次命中缓存而跨请求的重复读取则完全由revalidateTTL 覆盖。这正是“请求级去重”与“跨请求缓存”两种手段在真实大型 Next.js 应用中如何协同的例证。五、落地建议小结结合规则文档与仓库实践可以给出可操作清单优先包装高频公共依赖会话/认证解析、当前用户查询、组织/团队读取等扇出最广的函数是React.cache()的第一梯队候选规则原文认证与数据库查询受益最大。认清作用域它只对单次请求内的多次调用去重请求之间零共享不要把业务正确性建立在“它缓存了结果”上。异步函数放心用cache()包裹async函数时同一请求内的并发调用共享同一个 Promise不会各自开查询。跨请求需求另选方案连续操作命中多端点、且数据在数秒内可复用用带 TTL 与容量上限的 LRU见 server-cache-lru.mdNext.js 应用也可直接使用unstable_cacherevalidateTag的仓库式封装参照 unstable_cache 封装 与 membership 缓存。缓存键与失效策略要显式无论哪种缓存都建议像 cal.com 那样为每个缓存定义命名标签并在数据变更点如成员关系变化调用定向失效避免依赖 TTL 自然过期造成的数据陈旧窗口。参考路径规则原文.opencode/skill/vercel-react-best-practices/rules/server-cache-react.md规则库总览.opencode/skill/vercel-react-best-practices/SKILL.md、汇编版 .opencode/skill/vercel-react-best-practices/AGENTS.md姊妹规则.opencode/skill/vercel-react-best-practices/rules/server-cache-lru.md仓库实践packages/lib/unstable_cache/unstable_cache.ts、apps/web/app/cache/membership.ts、packages/lib/constants.ts【免费下载链接】cal.diyScheduling infrastructure for absolutely everyone.项目地址: https://gitcode.com/GitHub_Trending/ca/cal.diy创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/8 19:49:39

SVM实战:银行客户流失预测完整流程与调参指南

简介:压缩包以银行客户流失预测为案例,完整展示支持向量机(SVM)算法在分类问题上的落地流程,适合机器学习初学者、金融数据分析人员及有课程设计需求的学生。压缩包内共4个文件,包括2个CSV数据集、1个Pytho…

2026/9/8 19:49:39

简单并差集在学生管理系统中的应用

在最近的学习中,我对并查集学得一般,懂其原理,但用得不深,不过在最近Java的期末项目里,我的主题是高考模式下的学生成绩管理系统,我的思考就停留在了新高考的312的选科目上,那是不是同组合的人要…

2026/9/8 20:49:54

Hermes:基于大模型的自动化代码评审工具实践指南

先把结论放前面:我自己在 GitHub 仓库上跑过一段时间的 Hermes,它不只是一个 PR 辅助小玩具,而是能把“开 PR → 读 diff → 给评论 → 挂状态”这整条链路交给自动化代码评审去执行的一整套方案。如果你还在靠人工逐条翻 Pull Request&#…

2026/9/8 20:49:54

MAX31855热电偶信号调理芯片原理与工业应用指南

简介:本资源是一套基于STM32F4平台的MAX31855热电偶温度检测完整嵌入式工程,面向嵌入式开发初学者与工业测温应用开发者,解决热电偶高精度测温中冷端补偿、SPI通信驱动、异常诊断及低功耗管理等核心实现难题。包内共193个文件,涵盖…

2026/9/8 20:49:53

Claude Code完全配置实战:从安装、MCP到Skills全攻略

1. 整体认知框架:Claude Code 到底解构到哪一步了先说结论:这篇文章是这个系列的收尾篇,也是我认为最重要的一篇。前面十几篇我们分别聊了 Claude Code 的安装流程、CLI 参数调优、MCP 服务器接入、VSCode 插件联动、本地模型切换、Token 消耗…

2026/9/8 20:49:53

STM32F4工业级I2C驱动PCAP04电容传感器实战指南

简介:本资源是一份面向嵌入式开发工程师与物联网硬件工程师的I2C通信实战参考方案,聚焦Cuptime2主控平台与PCAP04触摸控制器之间的可靠交互实现。资源系统梳理了I2C协议配置要点(时钟频率、引脚复用、从机地址设定)、通信流程&…

2026/9/8 20:49:53

阿里开源skill-up:Agent Skill评测工具实战指南

写评测脚本、造评测数据,到头来发现最大的瓶颈根本不是模型能力,而是没法量化评估“这组配置到底比之前好在哪里”。尤其是Agent应用里大量使用Skill(技能)的时候,问题更明显:同一个问题,今天跑…

2026/9/8 20:44:52

零基础跑通金融风控系统:贷款违约预测实战指南

简介:本资源是阿里云出品的「零基础入门金融风控—贷款违约预测」实战课程包,面向Python初学者及金融科技入门学习者,聚焦信贷风控核心场景,系统讲解如何利用机器学习建模识别高风险贷款申请者。压缩包共58.83MB,含完整…

2026/9/8 7:15:10

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/8 7:15:15

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/8 7:15:10

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/8 0:01:49

踩多轮坑才跑通|OpenClaw 3.1.0 双平台本地 AI 自动化搭建实操实录

🔹 工具简述 OpenClaw 是一款备受开发者与办公人群青睐的开源本地智能工具,凭借离线本地运行、可视化图形面板、全流程自主任务处理三大核心特点,积累了众多忠实用户。与普通对话类 AI 产品不同,它能够直接调用电脑的软硬件操作权…

2026/9/8 0:01:50

拒绝复杂命令行,Hermes Agent 一键包快速解锁智能办公能力

🔍前言 不少想要体验 Hermes Agent 办公能力的使用者,往往会被复杂的环境配置拦住使用脚步。手动下载匹配依赖、反复调整系统目录、处理命令行持续报错、修复权限异常、补全丢失核心文件等一系列操作,对普通使用者而言门槛较高,很…

2026/9/7 16:23:03

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/7 22:46:00

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/7 22:45:59

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码