mirror of
https://github.com/neondatabase/neon.git
synced 2026-07-05 21:20:37 +00:00
I've moved it to the API handler in the 589cf1ed2 to do not delay
compute start. Yet, we now skip full configuration and catalog updates
in the most hot path -- waking up suspended compute, and only do it at:
- first start
- start with applying new configuration
- start for availability check
so it doesn't really matter anymore.
The problem with creating the table and test record in the API handler
is that someone can fill up timeline till the logical limit. Then it's
suspended and availability check is scheduled, so it fails.
If table + test row are created at the very beginning, we reserve a 8 KB
page for future checks, which theoretically will last almost forever.
For example, my ~1y old branch still has 8 KB sized test table:
```sql
cloud_admin@postgres=# select pg_relation_size('health_check');
pg_relation_size
------------------
8192
(1 row)
```
---------
Co-authored-by: Anastasia Lubennikova <anastasia@neon.tech>
69 lines
2.0 KiB
Rust
69 lines
2.0 KiB
Rust
use anyhow::{anyhow, Ok, Result};
|
|
use postgres::Client;
|
|
use tokio_postgres::NoTls;
|
|
use tracing::{error, instrument};
|
|
|
|
use crate::compute::ComputeNode;
|
|
|
|
/// Create a special service table for availability checks
|
|
/// only if it does not exist already.
|
|
pub fn create_availability_check_data(client: &mut Client) -> Result<()> {
|
|
let query = "
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS(
|
|
SELECT 1
|
|
FROM pg_catalog.pg_tables
|
|
WHERE tablename = 'health_check'
|
|
)
|
|
THEN
|
|
CREATE TABLE health_check (
|
|
id serial primary key,
|
|
updated_at timestamptz default now()
|
|
);
|
|
INSERT INTO health_check VALUES (1, now())
|
|
ON CONFLICT (id) DO UPDATE
|
|
SET updated_at = now();
|
|
END IF;
|
|
END
|
|
$$;";
|
|
client.execute(query, &[])?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Update timestamp in a row in a special service table to check
|
|
/// that we can actually write some data in this particular timeline.
|
|
#[instrument(skip_all)]
|
|
pub async fn check_writability(compute: &ComputeNode) -> Result<()> {
|
|
// Connect to the database.
|
|
let (client, connection) = tokio_postgres::connect(compute.connstr.as_str(), NoTls).await?;
|
|
if client.is_closed() {
|
|
return Err(anyhow!("connection to postgres closed"));
|
|
}
|
|
|
|
// The connection object performs the actual communication with the database,
|
|
// so spawn it off to run on its own.
|
|
tokio::spawn(async move {
|
|
if let Err(e) = connection.await {
|
|
error!("connection error: {}", e);
|
|
}
|
|
});
|
|
|
|
let query = "
|
|
INSERT INTO health_check VALUES (1, now())
|
|
ON CONFLICT (id) DO UPDATE
|
|
SET updated_at = now();";
|
|
|
|
let result = client.simple_query(query).await?;
|
|
|
|
if result.len() != 1 {
|
|
return Err(anyhow::format_err!(
|
|
"expected 1 query result, but got {}",
|
|
result.len()
|
|
));
|
|
}
|
|
|
|
Ok(())
|
|
}
|