【问题标题】:Actix Rust deadpool_postgres: DB connections not releasedActix Rust deadpool_postgres:未释放数据库连接
【发布时间】:2020-07-12 21:00:39
【问题描述】:

我有一个连接到 Postgres 数据库的 Actix-web 服务器。

我注意到在 1000 次请求后,我的 Postgres DB 的 RAM 使用率飙升

当我停止 actix-web 时,db 持有的 RAM 被清除。这让我相信我的代码没有释放连接。

我找不到实际释放连接的示例。看起来它是在其他人的代码中推断出来的。

这是我的:

async fn hellow_world(a : f32, b : f32, pool: &Pool) -> Result<Value, PoolError> {
    let client: Client = pool.get().await?;
    let sql = format!("select \"json\" from public.table_a WHERE a={} and b={}", a, b);
    let stmt = client.prepare(&sql).await?;
    let row = client.query_one(&stmt, &[]).await?;
    let result : Value = row.get(0);
    Ok(result)
}

#[derive(Deserialize)]
pub struct MyRequest {
   a: f32,
   b: f32
}

#[get("/hello")]
async fn sv_hellow_world(info: web::Query<MyRequest>, db_pool: web::Data<Pool>) -> Result<HttpResponse, Error> {
    let response : Value = hellow_world(info.a, info.b, &db_pool).await?;
    Ok(HttpResponse::Ok().json(response))
}

#[actix_rt::main]
async fn main() -> std::io::Result<()> {
    dotenv().ok();
    let config = Config::from_env().unwrap();
    let pool = config.pg.create_pool(tokio_postgres::NoTls).unwrap();

    env_logger::from_env(Env::default().default_filter_or("info")).init();

    let server = HttpServer::new(move || App::new().wrap(Logger::default()).wrap(Logger::new("%a %{User-Agent}i")).data(pool.clone()).service(sv_hellow_world))
        .bind("0.0.0.0:3000")?
        .run();
    server.await
}

【问题讨论】:

  • 从文档中不清楚prepare() 是否创建了服务器端prepared statement。如果是这样,那么这些将堆积在服务器上的共享连接中,因为您使用的是字符串格式而不是绑定参数。如果您可以放入调试查询,请查看 select * from pg_prepared_statements 的返回结果
  • 运行我所有的 HTTP 请求并且 RAM 仍然被分配,即使所有请求都是服务,我做了那个选择并得到 0 行返回
  • 您是否使用相同的数据库连接/会话进行操作?准备好的语句仅在声明它们的会话中可见。如果你这样做了,那么对不起猜测错误。
  • @MikeOrganek,看来你是对的!在每个唯一的 ab 值查询之后,我看到 pg_prepared_statements 的计数在增加。你会建议我如何解决这个问题?

标签: postgresql rust rust-actix actix-web


【解决方案1】:

根据进一步的测试,@Werner 确定代码堆积了服务器端准备好的语句。

不清楚这些语句是否可以使用这个库关闭。

可以使用两种方法中的任何一种来避免此问题:

  1. 使用单个共享的预处理语句
  2. 使用直接查询表单而不是准备好的语句

原则上我推荐第一种方法,因为它更有效并且可以防止 SQL 注入。它应该看起来像这样:

async fn hellow_world(a : f32, b : f32, pool: &Pool) -> Result<Value, PoolError> {
    let client: Client = pool.get().await?;
    let stmt = client.prepare("select \"json\" from public.table_a WHERE a=$1::numeric and b=$2::numeric").await?;
    let row = client.query_one(&stmt, &[&a, &b]).await?;
    let result : Value = row.get(0);
    Ok(result)
}

使用此代码,只应在池的每个连接上创建一个准备好的语句。

【讨论】:

  • 我已经尝试了一段时间。我收到一条错误消息,指出 ERROR: could not determine data type of parameter $1 注意:您的代码需要取消引用 ab
  • @Werner 你能像我更新的答案那样尝试使用演员表吗?另外,取消引用是否正确?如果没有,您可以编辑我的答案,我会批准。
  • 非常感谢您的帮助!遇到一个新错误,我必须转换的类型是int,因此是Internal Server Error: PoolError(Backend(Error { kind: ToSql(1), cause: Some(WrongType { postgres: Int4, rust: "i16" }) }))
  • @Werner 我认为f32 是浮点类型。您可以尝试在查询中转换为realint2 吗?如果您达到了要踢的地步,那么您可以将client.query() 技术与您的格式化字符串一起使用:rust-lang-nursery.github.io/rust-cookbook/database/…
猜你喜欢
  • 2015-03-26
  • 1970-01-01
  • 2017-12-24
  • 2016-05-11
  • 1970-01-01
  • 2014-08-06
  • 1970-01-01
  • 2021-08-20
  • 1970-01-01
相关资源
最近更新 更多