当 Postgres 数据中的某些条件满足时,运行缓慢的后台进程?

Eva*_*nss 5 postgresql node.js next.js vercel supabase

我正在使用 Postgres(通过 Supabase)和 Node(通过 Vercel 上的 NextJS)。

我有一个表report,其中包含枚举列region,并且generated_text是由人工智能生成的内容。

create type region as enum (
  'America',
  'Europe',
  'Asia',
  'Africa'
);

create table report (
  id serial primary key,
  region region,
  generated_text: text
);
Run Code Online (Sandbox Code Playgroud)

用户选择他们的区域,然后购买报告。购买记录在连接表中report_user

create table report_user(
  id serial primary key,
  report uuid NOT NULL,
  user: uuid NOT NULL
)
Run Code Online (Sandbox Code Playgroud)

当用户进行购买时:

  • 如果该区域的表中存在report用户之前未购买过的行,则该行将返回给他们。
  • 如果该地区没有报告,或者用户已经购买了该地区的所有报告,那么我们需要创建一个新行。这需要向列发送 AI API 请求,generated_text该请求非常慢并且用户体验很差。

我想提高用户的速度,这只能通过report在需要之前创建行来完成。我可以批量创建一些,但我不知道哪些区域会更受欢迎(我简化了我的示例,我的实际应用程序有更多的过滤器,因此有很多潜在的组合)。

report 当用户购买了report特定过滤器的所有行时,有没有办法运行后台任务来创建新任务region

Zeg*_*rek 2

有没有办法运行后台任务来创建...时...

是的,有 - 而且您甚至无需走出 Supabase 一步。

它提供了http扩展,让您可以直接从 SQL 中的 ,​​ 运行 API 请求来获取新报告,以在库存不足时补充您的库存trigger。甚至还有pg_net异步的方式。db<>fiddle 上的演示:

设置一个表,每当有人订购报告时,您都会在其中插入一行:

create table orders(id int generated always as identity primary key,
                    "user" uuid references users(uid), 
                    region region);
Run Code Online (Sandbox Code Playgroud)

另一种参数指示您何时要为每个报告补充库存region

create table report_stock_parameters as
select unnest(enum_range(null::region)) as region, 
       3 as min_stock_size,
       4 as restock_batch_size;
Run Code Online (Sandbox Code Playgroud)

您可以将告诉您何时补货的标准放在函数中:

create function is_restock_required(p_user uuid,p_region region)
returns boolean as $f$
    select (select min_stock_size 
            from report_stock_parameters
            where region=p_region) 
          >(select count(*) --counting reports unused by the user
            from report 
            left join report_user
                   on report.id=report_user.report
                  and report.region=p_region
                  and report_user.user=p_user
            where report_user.id is null) --anti-join
$f$ language sql;
Run Code Online (Sandbox Code Playgroud)

最后是调用API补货报表的流程:

create procedure fetch_some_more_reports(p_region region)as $f$
    insert into report (region,generated_text)
    select p_region, (response."content")::jsonb #>> '{path,to,report}'
    from generate_series(1,(select restock_batch_size 
                            from report_stock_parameters
                            where region=p_region))as a(g1),
         http_get('https://generative_ai_api.com/reports/please',
                   jsonb_build_object('access_token', 'eyJ0eXAiOiJKV',
                                      'throwaway', g1))as response;
$f$ language sql;
Run Code Online (Sandbox Code Playgroud)

响应插入新的传入订单而触发的触发器,以及当您需要补充时,运行补货过程:

create function trgf_restock_reports()returns trigger as $f$
begin 
    call fetch_some_more_reports(new.region);
    return null;
end $f$ language plpgsql;

create trigger t_report_stock_monitor after insert on orders
for each row when (is_restock_required(new.user,new.region))
execute function trgf_restock_reports();
Run Code Online (Sandbox Code Playgroud)

根据您的 API 希望您进行身份验证的方式,您可以将发送access_token有效负载更改为在标头、URL 参数等中传递密钥。请参阅扩展程序的 GitHub 上的示例

我在调用中填充g1值,以防止数据库仅对其进行一次评估并在重新填充期间重复使用您想要批量生成的每个报告。该依赖关系表明您想要多次获取新报告。generate_series()http_get()

您的 API 可能提供一种在一个请求中请求多个请求的方法,在这种情况下,您可以跳过generate_series(),直接传递restock_batch_size到请求中,然后将响应拆分jsonb_array_elements_text()为单独的行。


  1. Supabase 还支持pg_cron扩展,您可以使用它来设置重复性作业,也可以在数据库中进行。Atrigger听起来更好,因为只要有人订购报告,它就会触发,并立即从 API 获取报告以重新库存。重复性的、基于时间的pg_cron工作可能会面临这样的风险:有时您会用完库存,并且必须等待一段时间才能根据计划检查并重新库存。

  2. 如果它是您自己的纯 PostgreSQL,您可以使用Steampipe插件来获取报告的 API,该插件隐藏 API 调用逻辑的所有细节,将其公开为表格,并转换您的简单选择它转换为 API 调用,然后将响应转换为行,就好像它们来自表一样。

    您可以使用现有的、现成的用于生成 AI 的 Steampipe 插件(CohereAIOpenAI):

    select completion
    from openai_completion
    where prompt = 'Write a tagline for an ice cream shop.';
    
    Run Code Online (Sandbox Code Playgroud)
    完成
    1. 来一勺我们美味的冰淇淋降温!
    2. 用奶油来满足你的渴望!
    3. 用一勺我们的美味佳肴让您的一天变得甜蜜!

    如果您正在使用其他服务,则必须为其构建自己的 Steampipe 包装器。

    在 Supabase 中,您无法添加全尺寸的 Steampipe,但您可以让 Steampipe 在其他地方运行,并postgres_fdw通过foreign table. 这将为您提供一个完全声明式的、纯 SQL 的数据库内解决方案,可能将 API 处理卸载到 Steampipe,但需要注意的是需要两个数据库:pg_cronSupabase 上的计划或基于触发器的逻辑指示何时运行重新填充,它的执行方式是只需从另一个链接的数据库中运行选择,Steampipe 将其转换为 API 调用。

  3. Supabase 也有自己的wrappers框架,提供与 Steampipe 类似的方法。它只有12 个现成可用的选项,没有一个用于生成 AI(与 Steampipe 的 140 个选项相比,其中有 2 个),但它是原生的,因此如果您要尝试构建自己的 API 包装扩展,那么它可能是更好的选择。似乎有一种方法可以构建“Steampipe postgres fdw”并将其添加到 Supabase,但似乎 Supabase 只欢迎来自预配置列表之外的普通 SQL 或 PL/pgSQL 扩展,以及 Steampipe 生成的 FDW比这更复杂一些。

  4. 收集统计数据谁需要补充以及多久需要补充一次,并增加指示您希望领先于他们的需求多远的参数,这可能是一个好主意。根据 API 的工作方式、您正在获取的流量以及存储/数据库配置,您可能希望让触发器将用户分类为绿色、黄色和红色组,并且当用户仅从绿色下降到黄色时重新填充任何人的报告,但一旦至少有一个变成红色,就为每个人运行一次批量补充,无论是黄色还是红色。一个大请求,insert时不时一个大请求,取代了源源不断的小请求。