From f2c21447cec25280af8701b2ada50bcb04896336 Mon Sep 17 00:00:00 2001 From: Alexey Kondratov Date: Wed, 30 Aug 2023 17:44:28 +0200 Subject: [PATCH] [compute_ctl] Create check availability data during full configuration (#5084) 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 --- compute_tools/src/checker.rs | 39 ++++++++++++++++++++++++++++------- compute_tools/src/compute.rs | 2 ++ control_plane/src/endpoint.rs | 10 +++++++-- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/compute_tools/src/checker.rs b/compute_tools/src/checker.rs index b6a287bdeb..6f52004fa8 100644 --- a/compute_tools/src/checker.rs +++ b/compute_tools/src/checker.rs @@ -1,12 +1,39 @@ -use anyhow::{anyhow, Result}; +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. -/// Create table if it's missing. #[instrument(skip_all)] pub async fn check_writability(compute: &ComputeNode) -> Result<()> { // Connect to the database. @@ -24,19 +51,15 @@ pub async fn check_writability(compute: &ComputeNode) -> Result<()> { }); let query = " - CREATE TABLE IF NOT EXISTS 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();"; let result = client.simple_query(query).await?; - if result.len() != 2 { + if result.len() != 1 { return Err(anyhow::format_err!( - "expected 2 query results, but got {}", + "expected 1 query result, but got {}", result.len() )); } diff --git a/compute_tools/src/compute.rs b/compute_tools/src/compute.rs index 03ae39f79f..5c08ebe06a 100644 --- a/compute_tools/src/compute.rs +++ b/compute_tools/src/compute.rs @@ -27,6 +27,7 @@ use utils::measured_stream::MeasuredReader; use remote_storage::{DownloadError, GenericRemoteStorage, RemotePath}; +use crate::checker::create_availability_check_data; use crate::pg_helpers::*; use crate::spec::*; use crate::sync_sk::{check_if_synced, ping_safekeeper}; @@ -696,6 +697,7 @@ impl ComputeNode { handle_role_deletions(spec, self.connstr.as_str(), &mut client)?; handle_grants(spec, self.connstr.as_str())?; handle_extensions(spec, &mut client)?; + create_availability_check_data(&mut client)?; // 'Close' connection drop(client); diff --git a/control_plane/src/endpoint.rs b/control_plane/src/endpoint.rs index 7ebcf98ab0..4ed03c8771 100644 --- a/control_plane/src/endpoint.rs +++ b/control_plane/src/endpoint.rs @@ -138,7 +138,13 @@ impl ComputeControlPlane { mode, tenant_id, pg_version, - skip_pg_catalog_updates: false, + // We don't setup roles and databases in the spec locally, so we don't need to + // do catalog updates. Catalog updates also include check availability + // data creation. Yet, we have tests that check that size and db dump + // before and after start are the same. So, skip catalog updates, + // with this we basically test a case of waking up an idle compute, where + // we also skip catalog updates in the cloud. + skip_pg_catalog_updates: true, }); ep.create_endpoint_dir()?; @@ -152,7 +158,7 @@ impl ComputeControlPlane { http_port, pg_port, pg_version, - skip_pg_catalog_updates: false, + skip_pg_catalog_updates: true, })?, )?; std::fs::write(