尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Wing 语言 inflight Handler 单例语义剖析:以 `inflight_handler_singleton.test.w` 快照测试为例
编程语言编译器云原生语言运行时【免费下载链接】wingA programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡项目地址https://gitcode.com/gh_mirrors/wi/wing点击查看免费下载导读本文以 Wing 开源仓库中的真实测试用例 tests/valid/inflight_handler_singleton.test.w 及其 Hangar 测试快照 inflight_handler_singleton.test.w_test_sim.md 为核心素材深入讲解 Wing 语言中inflight 对象如自定义类实例在 cloud.Function 多次调用之间的单例与状态隔离语义。读完本文你将理解为什么同一个 Foo 实例在模拟器中会被复用、在真实云上却不可信赖以及如何写出与平台无关、行为可预测的 inflight 代码。一、快照文档是什么Hangar 测试套件与 Markdown 快照在 Wing 仓库中tools/hangar是面向整个 Wing 工具链的端到端测试套件。它会把tests/valid、tests/sdk_tests等目录下的.w测试文件分别针对不同目标平台如sim、tf-aws执行编译或测试并把产物固化为 Markdown 快照文件。相关实现见 tools/hangar/src/generated_test_targets.tscompile场景输出编译产物生成的 JS 文件、Terraform 配置等test场景则把wing test命令的 stdout/stderr 记录为stdout.log如 createMarkdownSnapshot 所示。本文关联文档就是testsim模拟器场景的快照[INFO] single instance of Foo | client has been reused pass ─ inflight_handler_singleton.test.wsim » root/Default/test:Foo state is not shared between function invocations pass ─ inflight_handler_singleton.test.wsim » root/Default/test:single instance of Foo Tests 2 passed (2) Snapshots 1 skipped Test Files 1 passed (1) Duration DURATION该快照记录了wing test在 sim 目标上的完整运行结果两个测试全部通过并打印了一条关键日志single instance of Foo | client has been reused。快照中的DURATION是 Hangar 在做快照比对时剔除的不稳定字段。二、测试源码逐段拆解一个 Foo 实例与三个 Function快照对应的被测程序 tests/valid/inflight_handler_singleton.test.w 是整个分析的主干它刻意构造了同一 inflight 对象被多个 Function、多次调用的场景。2.1 定义一个带可变状态的 inflight 类bring cloud; bring expect; bring util; class Foo { inflight var n: num; inflight new() { this.n 99; } pub inflight inc(): num { this.n 1; return this.n; } pub inflight get(): num { return this.n; } } let foo new Foo();要点inflight var n声明了一个 inflight 可变字段它只存在于运行时inflight 环境预检preflight阶段不可访问inflight new()是自定义初始化逻辑实例创建时把n初始化为99inc()自增并返回新值get()返回当前值都是pub inflight方法let foo new Foo()在 preflight 作用域创建了唯一一个Foo 实例之后它会被提升lift到各个闭包中。2.2 两个 Function 捕获同一个 foolet fn new cloud.Function(inflight () { let n foo.inc(); return {n}; }); let fn2 new cloud.Function(inflight () { let n foo.inc(); return {n}-fn2; }) as fn2;fn与fn2是两个独立的cloud.Function但它们的 inflight 闭包都捕获了同一个foo。这里用as fn2显式覆盖资源 ID保证编译产物中两个 Lambda 的名称可区分fn2而非自动生成的Function2。另外注意fn的闭包返回{n}fn2返回{n}-fn2用于区分返回值来自哪个函数。2.3 测试一验证单实例test single instance of Foo { let x fn.invoke(); let y fn.invoke(); let z fn2.invoke(); expect.equal(x, 100); // the simulator intentionally reuses the sandbox across invocations // but we cant trust that this will always happen on the cloud if target sim { expect.equal(y, 101); log(client has been reused); } expect.equal(z, 100-fn2); // fn2 should have a separate instance }断言逻辑与注释揭示了核心语义x 100第一次调用fnfoo.inc()把n从99变成100y 101第二次调用fn得到101——仅在 sim 目标上成立。测试源码注释明确说明the simulator intentionally reuses the sandbox across invocations模拟器故意在多次调用之间复用沙箱但同时强调we cant trust that this will always happen on the cloud不能指望云端总是如此z 100-fn2fn2虽然是同一个foo但每次调用fn2时其闭包中的foo会被重新初始化inflight new()再次执行n 99所以第一次调用得到100与fn的第二次调用得到的101互不影响。第二个测试进一步验证状态隔离let fn3 new cloud.Function(inflight () { let n foo.inc(); util.sleep(3s); assert(n foo.get()); }) as fn3; test Foo state is not shared between function invocations { // start two invocations of fn, staggering them by 1 second fn3.invokeAsync(); util.sleep(1s); fn3.invoke(); }fn3的闭包先inc()把 n 加 1再睡 3 秒最后断言n foo.get()自己加出来的值必须和自己读到的一致。测试用invokeAsync先发起一次异步调用隔 1 秒后再同步调用一次模拟两个调用在时间上重叠的场景。结论是并发/重叠的多次调用之间不共享状态每次调用都有自己的foo实例。三、源码级印证模拟器如何复用沙箱快照日志中的client has been reused与同一个实例行为来自模拟器对cloud.Function的实现 packages/winglang/sdk/src/target-sim/function.inflight.ts。3.1 Worker 池与沙箱复用Function类维护了一个workers: Sandbox[]数组构造函数通过props.concurrency确定maxWorkers模拟器默认并发上限为 100见 packages/winglang/sdk/src/target-sim/function.ts 的this.concurrency props.concurrency ?? 100。每次invoke时通过 findAvailableWorker 选取或创建 workerprivate async findAvailableWorker(): PromiseSandbox | undefined { const worker this.workers.find((w) w.isAvailable()); if (worker) { return worker; // 复用空闲沙箱 } if (this.workers.length this.maxWorkers) { const newWorker await this.initWorker(); this.workers.push(newWorker); return newWorker; // 扩容新建沙箱 } return undefined; // 超并发invoke 会抛错 }关键点串行调用时同一个 Sandbox子进程会被反复命中并复用因此子进程内存中的Foo实例、n字段都得以保留——这就是y 101的根源。3.2 Sandbox 子进程的存活策略packages/winglang/sdk/src/shared/sandbox.ts 的SandboxOptions.timeout注释直接点明了设计意图If an invocation returns successfully, the child process continues running and can be reused for subsequent invocations.也就是说只要一次调用没有超时、没有异常退出子进程就不被杀死继续等待下一个process.on(message)消息。call()通过child.send({ fn, args })与子进程通信收到响应后把available置回truesandbox.ts#L246-L267从而允许下一次复用。public async call(fn: string, ...args: any[]): Promiseany { if (!this.available) { throw new SandboxMultipleConcurrentCallsError(); } this.available false; if (!this.child) { await this.initialize(); // 惰性创建子进程 } return new Promise((resolve, reject) { this.child!.send({ fn, args } as ProcessRequest); // ...收到响应后 this.available true }); }同时SandboxMultipleConcurrentCallsError的存在说明单个沙箱不允许多个调用并发执行而fn3测试中的并发调用正是通过 worker 池中的不同沙箱来承载的——这也解释了为什么重叠调用之间状态不共享。3.3 超时与异常会终止复用需要注意如果调用超时沙箱会被SIGTERM杀死并标记为不可用sandbox.ts#L307-L321 的setTimeout逻辑SandboxTimeoutError会被 function.inflight.ts#L94-L97 包装成 Function timed out 错误抛出。也就是说复用只在调用成功且未超时的前提下成立。四、编译产物对比tf-aws 目标下的真实形态同目录下还有一份编译快照 inflight_handler_singleton.test.w_compile_tf-aws.md它揭示了同一份源码在 AWS 目标上会被编译成什么。4.1 闭包与 Foo 的 inflight 实现快照中每个闭包都被编译为一个独立的 JS 模块inflight.$Closure1-1.cjs等闭包通过工厂函数注入被捕获的对象module.exports function({ $foo }) { class $Closure1 { async handle() { const n (await $foo.inc()); return String.raw({ raw: [, ] }, n); } } return $Closure1; }Foo本身也被编译成inflight.Foo-1.cjs字段n直接挂在实例上class Foo { async inc() { this.n 1; return this.n; } async get() { return this.n; } async $inflight_init() { this.n 99; } }$inflight_init正是inflight new()的产物——每次在某个函数运行时环境中创建 Foo 客户端时都会执行。4.2 三个独立的 Lambda 与各自的 IAM 角色main.tf.json中可以看到该测试最终生成三个aws_lambda_functionFunction、fn2、fn3各自配套独立的aws_iam_role、CloudWatch Log Group 与 S3 代码对象。以Function为例的关键配置aws_lambda_function: { Function: { architectures: [arm64], environment: { variables: { NODE_OPTIONS: --enable-source-maps, WING_FUNCTION_NAME: Function-c852aba6, WING_TARGET: tf-aws } }, memory_size: 1024, runtime: nodejs20.x, timeout: 60, ... } }这组配置来自 packages/winglang/sdk/src/cloud/function.ts 中定义的FunctionProps默认值timeout默认 1 分钟tf-aws下以秒计即 60、memory默认 1024 MB、concurrency平台相关模拟器默认 100。用户可以这样覆盖let fn new cloud.Function(handler, timeout: 30s, memory: 512, concurrency: 2);4.3 为什么云上不能依赖状态延续在 AWS Lambda 中每次调用都由云平台调度到某个热实例warm container或冷启动实例上实例何时复用、何时回收完全不由用户代码控制。因此foo实例中n的初值99只在$inflight_init执行的那次调用内可靠跨调用是否延续取决于 Lambda 平台是否复用了同一执行环境。这正是测试源码注释强调不能信任云端总是复用、并把y 101的断言放在if target sim分支内的原因——该断言只在模拟器语义下成立。五、工程启示如何写出跨平台行为一致的 inflight 代码综合以上测试与源码可以提炼出几条实战准则默认无状态把cloud.Function的 handler 视为无状态函数任何跨调用依赖的计数、缓存都应存放在外部持久化资源如cloud.Bucket、cloud.Counter、数据库中而不是闭包捕获的 inflight 变量里。Wing SDK 恰好提供了cloud.Counter这类专门用于跨调用计数的资源。区分模拟器与云语义模拟器出于本地调试体验刻意复用沙箱这让热重放类行为在本地可复现但在编写测试断言时应像本测试一样用target分支显式区分平台避免把 sim 专属行为写成对所有目标都成立的硬断言。并发与状态隔离fn3测试证明同一 Function 的重叠调用各自持有独立的 Foo 客户端模拟器中表现为不同的 Sandbox。若要保证并发安全状态必须放在具备原子语义的外部资源中。善用快照做回归防护Hangar 会把这类语义测试固化到 tools/hangar/snapshots/test_corpus/valid 下的 Markdown 快照中。当模拟器沙箱复用策略、生成的 Terraform 配置或默认参数发生变化时快照比对会立即暴露差异防止静默破坏语义。修改tests/valid下的测试后可通过 Hangar 重新生成快照来同步更新。六、小结inflight_handler_singleton.test.w是 Wing 语言inflight 对象单例与状态隔离语义的最小完整标本测试源码定义了期望行为sim 快照记录了模拟器复用沙箱带来的实例延续tf-aws 快照则展示了真实云上三个独立 Lambda 的形态。三者对照可以清晰地得出结论——Wing 的 inflight 对象生命周期以单次调用为基本单位跨调用状态必须显式依赖外部资源模拟器只是提供了比云平台更宽松的调试体验而非语义保证。理解这一点是编写可靠、可移植的 Wing 云应用的前提。赞分享编程语言编译器云原生语言运行时【免费下载链接】wingA programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡项目地址https://gitcode.com/gh_mirrors/wi/wing点击查看免费下载相关推荐Wing 语言 inflight 类的阶段修饰符语义以 inflight_class_modifiers 测试快照为例Wing 语言 inflight 类的阶段修饰符语义以 inflight_class_modifiers 测试快照为例 导读 在 Wing 的云编程模型中编程语言编译器云原生语言运行时Wing 语言 inflight class 定义与编译产物剖析从测试用例到 tf-aws 快照Wing 语言 inflight class 定义与编译产物剖析从测试用例到 tf aws 快照 本文以 Wing 仓库中 inflight_class_de编程语言编译器云原生语言运行时Wing 语言测试快照解析在 inflight 闭包内定义 inflight class_test_sim 快照实战Wing 语言测试快照解析在 inflight 闭包内定义 inflight class_test_sim 快照实战 导读 本文以仓库中 Hangar 测编程语言编译器云原生语言运行时上一篇Voyager 隐藏 Gemini 首页最近项目与侧边栏 Gems 列表功能详解与源码实现解析下一篇EMQX e5.4.0 特性深度解析路由存储升级、安全增强与车联网网关落地创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED

相关推荐

ReAct 循环

ReAct 循环

ReAct 循环 1、基本介绍 ReAct 循环(Reasoning Acting)是一种让大语言模型交替进行推理和行动的 Agent 工作模式 2、循环结构 一个典型的 ReAct 循环包含四个阶段 思考:模型分析当前状态,决定下一步该做什么 例如:我需…

📅 2026/10/15 3:47:51
嵌入式可移植编程:从库函数雏形到分层设计,告别换板重写

嵌入式可移植编程:从库函数雏形到分层设计,告别换板重写

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/15 3:47:51
GLSL 噪声完全指南:《The Book of Shaders》第 11 章解析——从一维 Value Noise 到 Simplex Noise

GLSL 噪声完全指南:《The Book of Shaders》第 11 章解析——从一维 Value Noise 到 Simplex Noise

【免费下载链接】thebookofshaders Step-by-step guide through the abstract and complex universe of Fragment Shaders. 项目地址: https://gitcode.com/gh_mirrors/th/thebookofshaders 点击查看 免费下载 导读 噪声(Noise)是计算机图形…

📅 2026/10/15 3:47:51
MORE NEWS

更多资讯

📰

APQP平台化引擎:汽车电子研发管理如何告别流程灰区

做了十几年汽车电子研发的项目管理,反复验证出来的一个道理是:绝大多数项目延期,不是死在技术创新上,而是耗在流程的“灰区”里。APQP这套先期产品质量策划方法论,做研发的工程师几乎都背过五大阶段,但真到…

📰

华为题:编辑距离

给定两个字符串 word1 和 word2,请返回将 word1 转换成 word2 所使用的最少操作数。你可以对一个单词进行如下三种操作:插入一个字符删除一个字符替换一个字符输入描述第一行输入字符串 word1。第二行输入字符串 word2。输出描述输出最少操作数。示例 1输…

📰

螺旋矩阵II:用循环不变量与左闭右开区间搞定边界

最近在重新过代码随想录的数组专题,刷到 LC 59 螺旋矩阵 II 的时候,我停下来认真想了一会儿。说实话这题标着中等难度,但第一次动手写的人,十个里有八个会在边界条件上翻车。题目要求非常直白:给你一个正整数 n&#x…

📰

Spring Boot宠物医院管理系统:从数据库设计到部署实战解析

1. 项目概述1.1 这是什么项目,解决什么问题一份宠物医院管理系统的毕业设计源码,技术栈锁定在 Spring Boot。做过毕设的都知道,管理系统类题目是整个毕业设计题库里最稳的选择——需求明确、模块清晰、工作量可控,答辩的时候也容易…

📰

从曲面细分到计算着色器:我的 Vulkan 实验记录

一、曲面细分(Tessellation)做了什么给鸭子模型加了一条细分管线。细分是在渲染管线里插两个新阶段:细分控制着色器(TCS,tessellation control)和细分求值着色器(TES,tessellation e…

📰

自由出入宇宙空间:是工程学奖励还是本体论属性?

“自由出入宇宙各空间”,这句话在主流叙事里几乎自动绑定了一套画面:巨大的推进器、弯曲的时空、层层叠叠的能量井,以及一代代人前赴后继的工程攀登。但标题提出了另一个更狠的猜想——这种自由,或许根本不是一个“工程学奖励”&a…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬