AWS 持久执行 ESLint 插件
AWS Lambda持久功能的ESLint规则. 他们报告破解回放的密码.
如何进行重放
一个能耐的函数通过回放工作. 等待后继续执行死刑时a 调用,重试,或失败,兰布达再次运行 你的处理器从一开始就. SDK然后走过代码,直到上次运行停止:
- 在已经完成的一步中,SDK返回了该步骤所保存的结果. 没错 不再运行步骤的函数 .
- 在第一次未完成的操作中,SDK运行并保存了结果.
因此,一步中的代码运行 当该步骤执行, 和代码外 步骤在每次重播时再次运行.
一步外的代码必须是决定性的. SDK识别每个耐用 第一次行动需要上下文,第二次,和 诸如此类。 在重放时, 它匹配每个调用到同一位置的已保存操作 . 鉴于同样的保存结果,您的代码必须按相同的顺序发出相同的呼叫. 任何能给出不同答案的第二次都属于一步之内. 这包括当前时间、随机数字、UUID和API调用结果 变化.
此插件中有两个规则保护此要求 : no-non-deterministic-outside-step
财务报告和审定财务报表 no-closure-in-durable-operations第三项规则, no-nested-durable-operations, (中文(简体) ),
检查操作的耐用性。 第四个规则,
require-durable-operation-in-child-context,报告一个没有
持久工作。 默认关闭.
所需资源
- ESLint 8.57 或后期,9或10,使用平面配置格式(
eslint.config.js) (中文(简体) ). CI测试ESLint 8.57,最新9和10.@typescript-eslint/parser8个需要 同样的ESLint版本. - 节点.js 22或后.
- TypeScript 文件的解析器, 例如
@typescript-eslint/parser。 。 。 .
插件不支持遗留的 eslintrc 格式 。 见 遗存 eslintrac 配置。 。 。 .
安装
npm install --save-dev @aws/durable-execution-sdk-js-eslint-plugin @typescript-eslint/parser
@typescript-eslint/parser 让ESLint读取 TypeScript 。 一个 JavaScript 唯一的工程
不需要这个。 删除 languageOptions.parser 线条来自以下示例.
使用量
将建议配置添加到您的 eslint.config.js它可以实现三项规则
报告代码破解,作为错误重放.
// eslint.config.js
import typescriptParser from "@typescript-eslint/parser";
import durableExecution from "@aws/durable-execution-sdk-js-eslint-plugin";
export default [
{
files: ["**/*.ts"],
languageOptions: { parser: typescriptParser },
},
durableExecution.configs["flat/recommended"],
];
在共同JS项目中,使用 require 相反:
// eslint.config.js
const typescriptParser = require("@typescript-eslint/parser");
const durableExecution = require("@aws/durable-execution-sdk-js-eslint-plugin");
module.exports = [
{
files: ["**/*.ts"],
languageOptions: { parser: typescriptParser },
},
durableExecution.configs["flat/recommended"],
];
建议配置没有 files 设置, 因此它适用于每个文件 ESLint
检查。 为了将它限制在您的持久功能上,将它扩散到您自己的对象中:
{
...durableExecution.configs["flat/recommended"],
files: ["src/handlers/**/*.ts"],
}
ESLint 8.57在发现平面配置时使用平面配置 eslint.config.js 或者说
eslint.config.mjs 文档。 ESLint 9和10总是使用.
你自己选规则
以您选中的名称注册插件, 然后打开您想要的规则 。 这个
示例使用名称 durable数字 :
// eslint.config.js
import durableExecution from "@aws/durable-execution-sdk-js-eslint-plugin";
export default [
{
files: ["**/*.ts"],
plugins: { durable: durableExecution },
rules: {
"durable/no-nested-durable-operations": "error",
"durable/no-closure-in-durable-operations": "error",
"durable/no-non-deterministic-outside-step": "warn",
"durable/require-durable-operation-in-child-context": "warn",
},
// languageOptions: { parser: ... } for TypeScript, as above
},
];
建议配置中的规则名称
推荐的配置将插件注册为 @aws/durable-functions. ESLint (英语)
在输出时显示该名称,并在 eslint-disable 注释 :
// eslint-disable-next-line @aws/durable-functions/no-nested-durable-operations
遗存 eslintrac 配置
插件无法从 eslintrac 文件装入, 如 .eslintrc.json. ESLint (英语)
添加前缀 eslint-plugin- 转到 eslintrac 文件的插件名称
然后找那个包裹。 对于这个包裹,它寻找
@aws/eslint-plugin-durable-execution-sdk-js-eslint-plugin,不存在.
ESLint 10移除了斯林特克.
早期版本的 README 显示 extends: ["plugin:@aws/durable-execution-sdk-js-eslint-plugin/recommended"]。这种设置
从来没有工作过。 如果你有项目仍然使用 Eslintrac , 请移动到平地
配置,或遵循 ESLint 的
移徙指南。 。 。 .
导出 configs.recommended 是这个配置的后遗症。 没错
已贬值,而且只保留在读其 rules 继续工作.
规则
头三道规则在 flat/recommended 作为错误.
require-durable-operation-in-child-context 并不是,因为它的代码报告
仍然重播正确。 要使用它,打开它,如显示
你自己选规则。 。 。 .
| 规则 | 报告的内容 |
|---|---|
no-nested-durable-operations | 在其运行的背景上开始了持久行动 |
no-closure-in-durable-operations | 在步骤外声明该步骤改变的变量 |
no-non-deterministic-outside-step | 一步外的非定型代码, 如时间和随机值 |
require-durable-operation-in-child-context (未建议) | 儿童背景,其功能不能使其环境持久运作 |
no-nested-durable-operations
防止在自己的持久操作中使用相同的上下文对象.
一个步骤是一个工作单位,它不能包含其他可持久操作. 到组
操作,在儿童背景下运行 runInChildContext,并召唤他们
儿童的背景.
已检查操作 : step, (中文(简体) ), wait, (中文(简体) ), waitForCallback, (中文(简体) ), waitForCondition, (中文(简体) ),
parallel, (中文(简体) ), map 财务报告和审定财务报表 runInChildContext。 。 。 .
QQ 错误
const handler = withDurableExecution(
async (event: any, context: DurableContext) => {
return await context.step("step1", async () => {
// Error: Cannot use the same context object inside its own operation
await context.wait({ seconds: 1 });
const result = await context.step("step2", async () => {
return "nested step";
});
return result;
});
},
);
对
// Use runInChildContext for nested operations
const handler = withDurableExecution(
async (event: any, context: DurableContext) => {
return await context.runInChildContext("block1", async (childCtx) => {
await childCtx.wait({ seconds: 1 });
const result = await childCtx.step("step2", async () => {
return "nested step";
});
return result;
});
},
);
已知的限制
该规则通过查找箭头函数的参数查找上下文
函数表达式,或用于文件中声明的变量。 没有
承认作为函数声明参数的上下文,例如
async function handler(event, context) { ... },所以它没有报告那里.
将处理器写作箭头函数,例如上面的例子,以获取
检查.
no-closure-in-durable-operations
防止在持久操作中修改关闭变量。 阅读结束 允许变量.
步骤的功能不会在回放时再次运行,所以它会改变为 步骤以外的变量不会在重放时发生。 步骤之后的代码 发现一个不同的值 在第一次运行时看到, 这使得代码 非决定性。 为了传递出一步中的值,从一步中返回.
已检查操作 : step, (中文(简体) ), runInChildContext, (中文(简体) ), waitForCondition 财务报告和审定财务报表
waitForCallback。 。 。 .
QQ 错误
const handler = withDurableExecution(
async (event: any, context: DurableContext) => {
let a = 2;
const result = await context.runInChildContext(
async (childContext: DurableContext) => {
const stepResult = await childContext.step(async () => {
// Error: this step does not run again on replay, so this change to 'a' is lost
a = a + 1;
return "child step completed";
});
return stepResult;
},
);
return result;
},
);
对
const handler = withDurableExecution(
async (event: any, context: DurableContext) => {
let a = 2;
const result = await context.runInChildContext(
async (childContext: DurableContext) => {
const stepResult = await childContext.step(async () => {
// Reading 'a' is OK
const value = a + 1;
return value;
});
return stepResult;
},
);
return result;
},
);
// Or use local variables
const handler = withDurableExecution(
async (event: any, context: DurableContext) => {
const result = await context.runInChildContext(
async (childContext: DurableContext) => {
const stepResult = await childContext.step(async () => {
let a = 2;
a = a + 1;
return "child step completed";
});
return stepResult;
},
);
return result;
},
);
no-non-deterministic-outside-step
要求非决定性代码在一步内运行. 一步外的代码运行 每次重播时 都要给出同样的答案 一个步骤的结果 被保存,因此重放会读取所保存的值,而不是再次运行调用.
规则规定这些调用在步骤回调之外时:
Math.random()Date.now()财务报告和审定财务报表new Date()没有参数performance.now()crypto.randomBytes()财务报告和审定财务报表crypto.getRandomValues()- 名字包含
uuid,例如crypto.randomUUID()或者说uuidv4()
规则检查电话写在哪里 包括帮手的尸体 在步骤外定义的函数。 它还向一个助手报告一个电话 当调用到 帮手在外面一步.
QQ 错误
const handler = withDurableExecution(
async (event: any, context: DurableContext) => {
// Error: this code runs again on every replay, and both values change
const orderId = crypto.randomUUID();
const startedAt = Date.now();
await context.step("charge", async () => chargeCard(orderId));
return { orderId, startedAt };
},
);
对
const handler = withDurableExecution(
async (event: any, context: DurableContext) => {
// The step saves these values, so a replay returns the same ones
const { orderId, startedAt } = await context.step(
"create-order",
async () => ({ orderId: crypto.randomUUID(), startedAt: Date.now() }),
);
await context.step("charge", async () => chargeCard(orderId));
return { orderId, startedAt };
},
);
require-durable-operation-in-child-context
要求儿童环境至少对其环境进行一次持久操作.
儿童的背景是: runInChildContext 传到它的功能.
map 将一个传递给每个项目的函数,并且 parallel 各递一个
树枝。 当函数时,子上下文会保存其函数的结果
结束。 重放返回以保存结果而不再调用函数 .
因此,这个功能在上下文上不要求持久操作,例如: step, (中文(简体) ),
invoke 或者说 wait,仍然重播正确。 它在两个方面与一步不同:
- 当它抛出时,一步来重复它的工作. 默认重试策略最多 6 个 尝试。 子上下文在第一个错误时失败 .
- 大于检查站尺寸限制的结果不适合孩子 上下文中的检查点。 然后SDK在每次重放时再次运行此功能 重建结果.
因此,规则要求你把作品放在一步.
该细则报告这些职能:
- 函数传递到
runInChildContext - 函数传递到
map - 每个分支传递给
parallel,作为函数或{ name, func }
QQ 错误
const handler = withDurableExecution(
async (event: any, context: DurableContext) => {
// Error: no durable operation runs on ctx
await context.runInChildContext("transform", async (ctx) =>
cheapTransform(event),
);
// Error: the same for each item
await context.map("transform", event.items, async (ctx, item) =>
cheapTransform(item),
);
},
);
对
const handler = withDurableExecution(
async (event: any, context: DurableContext) => {
// A step saves the result, and it retries the work if it throws
await context.step("transform", async () => cheapTransform(event));
// A child context that contains a durable operation
await context.map("transform", event.items, async (ctx, item) =>
ctx.step("compute", async () => cheapTransform(item)),
);
},
);
规则如何遵循上下文
该规则跟随上下文进入在同一文件中声明的函数. 没错
当代码通过上下文作为参数时, 例如 charge(ctx),以及
函数被声明为 function charge() {} 或者说 const charge = () => {}。 。 。 .
该规则不遵循文件再次指定的函数声明 .
当上下文到达它无法读取的代码时, 规则不报告任何信息 。 例如:
一个导入函数,一个方法调用,例如 service.run(ctx),返回值,一个
对象属性、休息参数、上下文前的扩展参数,以及
arguments.该代码可以进行持久操作.
已知的限制
map 财务报告和审定财务报表 parallel 是常用方法名称。 洛达什有 _.map比如说。 这么说吧
规则检查 a map 或者说 parallel 当它知道接收器是一个
持久环境。 接收器必须是下列参数之一:
- 附加说明类型
DurableContext,包括诸如import type { DurableContext as Ctx } - 函数的第二个参数
withDurableExecution,内置或 按名称 - a 的上下文参数
runInChildContext, (中文(简体) ),map或者说parallel函数
规则检查 runInChildContext 在任何接收器上,因为名称是特定的
给SDK.
为什么是这些规则?
在第一次运行时,在步外重放一次代码,并在每次运行时重放一次 重弹. SDK 只有在每次运行都相同的情况下才能正确恢复执行 同一顺序的持久通话. 以下前两项规则保护这一要求. 第三作保护了被巢操作的结构.
在一步之外没有非决定性代码
读取时钟或生成随机值的代码给出了不同的答案
每个运行。 如果运行在一步之外,重放可以计算出一个不同的值
第一次运行是。 一会儿 if 语句然后可以取取另一个分支,和代码
和SDK保存的电话不同 将呼叫包成
步骤保存其结果,所以每回放读取相同的值.
持久业务没有关闭
一个步骤的功能不会在回放时再次运行. 如果在外部更改变量 步骤,第一个运行看到变化,一个回放不。 后面的代码 步骤,然后在两个运行时使用不同的值。 从 步骤代替,因为 SDK 保存什么步骤返回.
读取关闭变量是安全的,因为它不会改变状态. 只有变异 (派任,递增,递减)有问题.
没有嵌入式持久操作
使用与其运行的操作相同的上下文的持久操作会导致 运行时间错误和出乎意料的行为。 SDK 期望分组工作在 儿童环境。 这一规则有助于在开发时抓住这些问题.
需要在儿童背景下开展持久行动
儿童环境组织持久行动。 无持久性的儿童背景 操作仍然保存它的结果,所以它可以正确重放。 这就是为什么规则 不在推荐配置中 。 一步步做同样的工作 重复,所以规则 指向一个步骤.
许可证
阿帕奇 - 2.0