Supabase Edge Functions持久化存储和97%更快的冷启动
Supabase Edge Functions持久化存储和97%更快的冷启动
引言
今天,我们为Edge Functions引入了持久化存储功能,并将冷启动时间提升了高达97%。之前,Edge Functions只支持通过写入/tmp目录进行临时文件存储。许多执行任务的常用库,如文件压缩/解压缩和图像转换,都是基于持久化文件存储构建的,因此让它们在Edge Functions中工作需要额外的步骤。
⚡️ 更多Launch Week信息
持久化存储功能
持久化存储选项基于S3协议构建。它允许你将任何S3兼容的存储桶(包括Supabase Storage Buckets)挂载为Edge Functions的目录。你可以像在POSIX文件系统中一样对挂载的存储桶执行读取和写入文件等操作。
- // 从S3存储桶读取
- const data = await Deno.readFile('/s3/my-bucket/results.csv')
- // 创建目录
- await Deno.mkdir('/s3/my-bucket/sub-dir')
- // 写入S3存储桶
- await Deno.writeTextFile('/s3/my-bucket/demo.txt', 'hello world')
如何配置
要从Edge Functions访问S3存储桶,你必须在Edge Function Secrets中设置以下环境变量:
S3FS_ENDPOINT_URL
S3FS_REGION
S3FS_ACCESS_KEY_ID
S3FS_SECRET_ACCESS_KEY
如果你使用Supabase Storage,请按照此指南启用并创建访问密钥和ID。
使用案例:Edge Functions中的SQLite
S3文件系统简化了涉及读取和转换存储在S3存储桶中数据的工作流程。
例如,假设你正在构建一个IoT应用程序,其中设备将其SQLite数据库备份到S3。你可以设置一个计划的Edge Function来读取这些数据,然后将数据推送到你的主Postgres数据库进行聚合和报告。
- // 以下示例为便于阅读而简化
- import { DB } from "https://deno.land/x/sqlite@v3.9.1/mod.ts";
- import { supabase } from '../shared/client.ts'
- const today = new Date().toISOString().split('T')[0]
- const backupDBPath = `backups/backup-${today}.db`
- // 使用S3 FS读取SQLite数据库
- const data = Deno.readFileSync(`/s3/${backupDBPath}`);
- // 从S3下载的数据创建内存中的SQLite
- // 这比直接从S3读取更快
- const db = new DB();
- db.deserialize(data);
- function calculateStats(rows: IoTData[], date: string): StatsSummary {
- // ....
- }
- Deno.serve(async (req)=>{
- // 假设IoT数据存储在名为'sensor_data'的表中
- const rows = db.queryEntries
(` - SELECT * FROM sensor_data
- WHERE date(timestamp) = date('now', 'localtime')
- `)
- // 计算统计信息
- const stats = calculateStats(rows, today)
- // 将统计信息插入Supabase
- const { data, error } = await supabase
- .from('iot_daily_stats')
- .insert([stats])
- return new Response("OK");
- });
97%更快的函数启动时间,即使在负载下
之前,具有大型依赖项或在启动时进行准备工作的Edge Functions(例如,解析/加载配置、初始化AI模型)会产生明显的启动延迟。有时,这些慢邻居会影响同一台机器上运行的其他函数。Supabase Edge Functions Runtime中的所有JavaScript workers都在同一个Tokio线程池上进行协作调度。如果一个worker有繁重的启动逻辑,例如解析JavaScript模块或运行同步操作,它可能会延迟之后调度的每个worker。这导致高流量项目中偶尔出现长尾延迟峰值。
为了解决这个问题,我们将仍在执行初始脚本评估的workers移到了专用的阻塞池中。这种方法防止繁重的初始化任务阻塞Tokio线程,显著减少了其他函数的启动时间峰值。
结果
启动时间现在更加可预测,冷启动的等待时间现在快得多。以下是我们进行的一项基准测试的结果,比较了这些更改前后的启动时间。
|
指标
|
之前
|
之后
|
(变化)
|
|
平均
|
870 ms
|
42 ms
|
95%
|
|
P95
|
8,502 ms
|
86 ms
|
99%
|
|
P99
|
15,069 ms
|
460 ms
|
97%
|
|
最差
|
24,300 ms
|
1,630 ms
|
93%
|
|
峰值 > 1s
|
47%
|
4%
|
43 pp
|
同步API支持
通过将函数启动时的昂贵计算卸载到单独的池中,我们能够在函数启动期间启用同步文件API的使用。一些库只支持同步文件API(例如SQLite),这将允许你在Edge Functions开始处理请求之前设置它们。
你现在可以在初始脚本评估期间安全地使用以下同步Deno API(及其Node对应项):
Deno.statSync
Deno.removeSync
Deno.writeFileSync
Deno.writeTextFileSync
Deno.readFileSync
Deno.readTextFileSync
Deno.mkdirSync
Deno.makeTempDirSync
Deno.readDirSync
请记住,同步API仅在初始脚本评估期间可用,在HTTP处理程序或setTimeout等回调中不支持。
- Deno.statSync('...') // ✅
- setTimeout(() => {
- Deno.statSync('...') // 💣 错误!Deno.statSync在当前上下文中被阻止
- })
- Deno.serve(() => {
- Deno.statSync('...') // 💣 错误!Deno.statSync在当前上下文中被阻止
- })
今天在Preview上试用
这些更改将与Deno 2升级一起在未来2周内推广到所有集群。同时,如果你想今天试用它们,可以使用Preview集群。请参阅此指南了解如何在Preview集群中测试你的函数。
Launch Week 15
主舞台
第1天 - 介绍JWT签名密钥
第2天 - 介绍支持Iceberg的Supabase Analytics存储桶
第3天 - 介绍Branching 2.0
第4天 - 介绍Supabase中的新可观测性功能
第5天 - 介绍Edge Functions的持久化存储
构建舞台
01 - Supabase UI: Platform Kit
02 - 使用Figma Make创建Supabase后端
03 - 介绍stripe-sync-engine npm包
04 - 改进的安全控制和安全的新家园
05 - Supabase的Algolia连接器
全球社区聚会
技术细节
持久化存储的优势
文件系统兼容性:支持标准文件操作
S3协议支持:与现有S3生态系统完全兼容
性能优化:减少网络延迟和传输开销
成本效益:利用现有的S3存储基础设施
冷启动优化原理
专用阻塞池:将初始化任务隔离到专用线程池
异步调度:避免阻塞主事件循环
资源隔离:防止单个函数影响其他函数
智能缓存:优化模块加载和依赖解析
实际应用场景
1. 文件处理管道
- // 图像处理示例
- import { Image } from "https://deno.land/x/imagescript@1.2.17/mod.ts";
- Deno.serve(async (req) => {
- // 从S3读取原始图像
- const originalImage = await Deno.readFile('/s3/uploads/original.jpg');
- // 处理图像
- const image = await Image.decode(originalImage);
- const resized = await image.resize(800, 600);
- const processed = await resized.encode();
- // 保存处理后的图像
- await Deno.writeFile('/s3/processed/resized.jpg', processed);
- return new Response('Image processed successfully');
- });
2. 数据分析和报告
- // 数据分析示例
- import { parse } from "https://deno.land/std@0.208.0/csv/mod.ts";
- Deno.serve(async (req) => {
- // 读取CSV数据
- const csvData = await Deno.readTextFile('/s3/data/sales.csv');
- const records = parse(csvData, { skipFirstRow: true });
- // 分析数据
- const totalSales = records.reduce((sum, record) =>
- sum + parseFloat(record.amount), 0);
- // 生成报告
- const report = {
- totalSales,
- recordCount: records.length,
- averageSale: totalSales / records.length
- };
- // 保存报告
- await Deno.writeTextFile('/s3/reports/daily-sales.json',
- JSON.stringify(report, null, 2));
- return new Response(JSON.stringify(report));
- });
3. 机器学习模型服务
- // ML模型服务示例
- import { load } from "https://deno.land/x/onnx_runtime/mod.ts";
- let model = null;
- // 在启动时加载模型
- try {
- const modelData = await Deno.readFile('/s3/models/sentiment-analysis.onnx');
- model = await load(modelData);
- } catch (error) {
- console.error('Failed to load model:', error);
- }
- Deno.serve(async (req) => {
- if (!model) {
- return new Response('Model not loaded', { status: 500 });
- }
- const { text } = await req.json();
- // 进行预测
- const prediction = await model.run({ input: text });
- return new Response(JSON.stringify({ prediction }));
- });
最佳实践
1. 错误处理
- Deno.serve(async (req) => {
- try {
- const data = await Deno.readFile('/s3/my-bucket/file.txt');
- return new Response(data);
- } catch (error) {
- console.error('File read error:', error);
- return new Response('File not found', { status: 404 });
- }
- });
2. 性能优化
- // 缓存文件内容
- let cachedData = null;
- Deno.serve(async (req) => {
- if (!cachedData) {
- cachedData = await Deno.readFile('/s3/my-bucket/large-file.json');
- }
- return new Response(cachedData);
- });
3. 批量操作
- Deno.serve(async (req) => {
- const files = ['file1.txt', 'file2.txt', 'file3.txt'];
- const results = [];
- for (const file of files) {
- try {
- const content = await Deno.readTextFile(`/s3/my-bucket/${file}`);
- results.push({ file, content });
- } catch (error) {
- results.push({ file, error: error.message });
- }
- }
- return new Response(JSON.stringify(results));
- });
迁移指南
从临时存储迁移
- // 旧方式 - 临时存储
- const tempFile = '/tmp/data.json';
- await Deno.writeTextFile(tempFile, JSON.stringify(data));
- // 新方式 - 持久化存储
- const persistentFile = '/s3/my-bucket/data.json';
- await Deno.writeTextFile(persistentFile, JSON.stringify(data));
环境变量配置
- # 在Supabase Dashboard中设置
- S3FS_ENDPOINT_URL=https://your-s3-endpoint.com
- S3FS_REGION=us-east-1
- S3FS_ACCESS_KEY_ID=your-access-key
- S3FS_SECRET_ACCESS_KEY=your-secret-key
总结
Supabase Edge Functions的持久化存储和冷启动优化为开发者提供了:
持久化文件存储:支持S3兼容的存储桶
显著的性能提升:冷启动时间减少97%
同步API支持:在启动期间支持同步文件操作
更好的可扩展性:资源隔离和智能调度
这些改进使得Edge Functions更适合处理文件密集型任务,如数据处理、图像处理和机器学习模型服务。
