mirror of
https://github.com/neondatabase/neon.git
synced 2026-01-14 08:52:56 +00:00
See #5001 No space is what's expected if we're at size limit. Of course if SK incorrectly returned "no space", the availability check wouldn't fire. But users would notice such a bug quite soon anyways. So ignoring "no space" is the right trade-off. ## Problem ## Summary of changes ## Checklist before requesting a review - [ ] I have performed a self-review of my code. - [ ] If it is a core feature, I have added thorough tests. - [ ] Do we need to implement analytics? if so did you add the relevant metrics to the dashboard? - [ ] If this PR requires public announcement, mark it with /release-notes label and add several sentences in this section. ## Checklist before merging - [ ] Do not forget to reformat commit message to not include the above checklist --------- Co-authored-by: Konstantin Knizhnik <knizhnik@neon.tech> Co-authored-by: Joonas Koivunen <joonas@neon.tech>
80 lines
2.3 KiB
Rust
80 lines
2.3 KiB
Rust
use anyhow::{anyhow, Ok, Result};
|
|
use postgres::Client;
|
|
use tokio_postgres::NoTls;
|
|
use tracing::{error, instrument, warn};
|
|
|
|
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();";
|
|
|
|
match client.simple_query(query).await {
|
|
Result::Ok(result) => {
|
|
if result.len() != 1 {
|
|
return Err(anyhow::anyhow!(
|
|
"expected 1 query results, but got {}",
|
|
result.len()
|
|
));
|
|
}
|
|
}
|
|
Err(err) => {
|
|
if let Some(state) = err.code() {
|
|
if state == &tokio_postgres::error::SqlState::DISK_FULL {
|
|
warn!("Tenant disk is full");
|
|
return Ok(());
|
|
}
|
|
}
|
|
return Err(err.into());
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|