mirror of
https://github.com/neondatabase/neon.git
synced 2026-05-22 07:30:37 +00:00
Turn off data-checksums for old tenants by default and explicitly enable for new ones
This commit is contained in:
@@ -427,6 +427,7 @@ impl PageServerNode {
|
||||
.map(|x| x.parse::<NonZeroU64>())
|
||||
.transpose()
|
||||
.context("Failed to parse 'max_lsn_wal_lag' as non zero integer")?,
|
||||
data_checksums: Some(true),
|
||||
})
|
||||
.send()?
|
||||
.error_from_body()?
|
||||
@@ -436,7 +437,7 @@ impl PageServerNode {
|
||||
.map(|id| {
|
||||
id.parse().with_context(|| {
|
||||
format!(
|
||||
"Failed to parse tennat creation response as tenant id: {}",
|
||||
"Failed to parse tenant creation response as tenant id: {}",
|
||||
id
|
||||
)
|
||||
})
|
||||
|
||||
@@ -38,6 +38,7 @@ pub struct TenantCreateRequest {
|
||||
pub walreceiver_connect_timeout: Option<String>,
|
||||
pub lagging_wal_timeout: Option<String>,
|
||||
pub max_lsn_wal_lag: Option<NonZeroU64>,
|
||||
pub data_checksums: Option<bool>,
|
||||
}
|
||||
|
||||
#[serde_as]
|
||||
|
||||
@@ -494,6 +494,8 @@ components:
|
||||
type: string
|
||||
compaction_threshold:
|
||||
type: string
|
||||
data_checksums:
|
||||
type: boolean
|
||||
TenantConfigInfo:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
@@ -412,6 +412,9 @@ async fn tenant_create_handler(mut request: Request<Body>) -> Result<Response<Bo
|
||||
tenant_conf.compaction_target_size = request_data.compaction_target_size;
|
||||
tenant_conf.compaction_threshold = request_data.compaction_threshold;
|
||||
|
||||
// Turn on data checksums for all new tenants
|
||||
tenant_conf.data_checksums = Some(request_data.data_checksums.unwrap_or(true));
|
||||
|
||||
if let Some(compaction_period) = request_data.compaction_period {
|
||||
tenant_conf.compaction_period =
|
||||
Some(humantime::parse_duration(&compaction_period).map_err(ApiError::from_err)?);
|
||||
|
||||
@@ -473,6 +473,7 @@ pub mod repo_harness {
|
||||
walreceiver_connect_timeout: Some(tenant_conf.walreceiver_connect_timeout),
|
||||
lagging_wal_timeout: Some(tenant_conf.lagging_wal_timeout),
|
||||
max_lsn_wal_lag: Some(tenant_conf.max_lsn_wal_lag),
|
||||
data_checksums: Some(tenant_conf.data_checksums),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,10 @@ pub mod defaults {
|
||||
pub const DEFAULT_WALRECEIVER_CONNECT_TIMEOUT: &str = "2 seconds";
|
||||
pub const DEFAULT_WALRECEIVER_LAGGING_WAL_TIMEOUT: &str = "10 seconds";
|
||||
pub const DEFAULT_MAX_WALRECEIVER_LSN_WAL_LAG: u64 = 10 * 1024 * 1024;
|
||||
|
||||
// Turn off data checksums by default to do not affect old tenants.
|
||||
// We turn it on explicitly for all new tenants.
|
||||
pub const DEFAULT_DATA_CHECKSUMS: bool = false;
|
||||
}
|
||||
|
||||
/// Per-tenant configuration options
|
||||
@@ -83,6 +87,7 @@ pub struct TenantConf {
|
||||
/// A lagging safekeeper will be changed after `lagging_wal_timeout` time elapses since the last WAL update,
|
||||
/// to avoid eager reconnects.
|
||||
pub max_lsn_wal_lag: NonZeroU64,
|
||||
pub data_checksums: bool,
|
||||
}
|
||||
|
||||
/// Same as TenantConf, but this struct preserves the information about
|
||||
@@ -105,6 +110,7 @@ pub struct TenantConfOpt {
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub lagging_wal_timeout: Option<Duration>,
|
||||
pub max_lsn_wal_lag: Option<NonZeroU64>,
|
||||
pub data_checksums: Option<bool>,
|
||||
}
|
||||
|
||||
impl TenantConfOpt {
|
||||
@@ -135,6 +141,7 @@ impl TenantConfOpt {
|
||||
.lagging_wal_timeout
|
||||
.unwrap_or(global_conf.lagging_wal_timeout),
|
||||
max_lsn_wal_lag: self.max_lsn_wal_lag.unwrap_or(global_conf.max_lsn_wal_lag),
|
||||
data_checksums: self.data_checksums.unwrap_or(global_conf.data_checksums),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,6 +179,9 @@ impl TenantConfOpt {
|
||||
if let Some(max_lsn_wal_lag) = other.max_lsn_wal_lag {
|
||||
self.max_lsn_wal_lag = Some(max_lsn_wal_lag);
|
||||
}
|
||||
if let Some(data_checksums) = other.data_checksums {
|
||||
self.data_checksums = Some(data_checksums);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,6 +209,7 @@ impl TenantConf {
|
||||
.expect("cannot parse default walreceiver lagging wal timeout"),
|
||||
max_lsn_wal_lag: NonZeroU64::new(DEFAULT_MAX_WALRECEIVER_LSN_WAL_LAG)
|
||||
.expect("cannot parse default max walreceiver Lsn wal lag"),
|
||||
data_checksums: DEFAULT_DATA_CHECKSUMS,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,6 +240,7 @@ impl TenantConf {
|
||||
.unwrap(),
|
||||
max_lsn_wal_lag: NonZeroU64::new(defaults::DEFAULT_MAX_WALRECEIVER_LSN_WAL_LAG)
|
||||
.unwrap(),
|
||||
data_checksums: defaults::DEFAULT_DATA_CHECKSUMS,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::tenant_config::TenantConfOpt;
|
||||
use crate::thread_mgr::ThreadKind;
|
||||
use crate::timelines::CreateRepo;
|
||||
use crate::walredo::PostgresRedoManager;
|
||||
use crate::{thread_mgr, timelines, walreceiver};
|
||||
use crate::{tenant_config, thread_mgr, timelines, walreceiver};
|
||||
use crate::{DatadirTimelineImpl, RepositoryImpl};
|
||||
use anyhow::{bail, Context};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -266,7 +266,11 @@ pub fn create_tenant_repository(
|
||||
Ok(None)
|
||||
}
|
||||
Entry::Vacant(v) => {
|
||||
let wal_redo_manager = Arc::new(PostgresRedoManager::new(conf, tenant_id));
|
||||
let data_checksums = tenant_conf
|
||||
.data_checksums
|
||||
.unwrap_or(tenant_config::defaults::DEFAULT_DATA_CHECKSUMS);
|
||||
let wal_redo_manager =
|
||||
Arc::new(PostgresRedoManager::new(conf, data_checksums, tenant_id));
|
||||
let repo = timelines::create_repo(
|
||||
conf,
|
||||
tenant_conf,
|
||||
@@ -567,10 +571,16 @@ fn load_local_repo(
|
||||
tenant_id: ZTenantId,
|
||||
remote_index: &RemoteIndex,
|
||||
) -> anyhow::Result<Arc<RepositoryImpl>> {
|
||||
// Restore tenant config
|
||||
let tenant_conf = LayeredRepository::load_tenant_config(conf, tenant_id)?;
|
||||
|
||||
let mut m = tenants_state::write_tenants();
|
||||
let tenant = m.entry(tenant_id).or_insert_with(|| {
|
||||
let data_checksums = tenant_conf
|
||||
.data_checksums
|
||||
.unwrap_or(tenant_config::defaults::DEFAULT_DATA_CHECKSUMS);
|
||||
// Set up a WAL redo manager, for applying WAL records.
|
||||
let walredo_mgr = PostgresRedoManager::new(conf, tenant_id);
|
||||
let walredo_mgr = PostgresRedoManager::new(conf, data_checksums, tenant_id);
|
||||
|
||||
// Set up an object repository, for actual data storage.
|
||||
let repo: Arc<LayeredRepository> = Arc::new(LayeredRepository::new(
|
||||
@@ -588,8 +598,6 @@ fn load_local_repo(
|
||||
}
|
||||
});
|
||||
|
||||
// Restore tenant config
|
||||
let tenant_conf = LayeredRepository::load_tenant_config(conf, tenant_id)?;
|
||||
tenant.repo.update_tenant_config(tenant_conf)?;
|
||||
|
||||
Ok(Arc::clone(&tenant.repo))
|
||||
|
||||
@@ -132,6 +132,7 @@ lazy_static! {
|
||||
pub struct PostgresRedoManager {
|
||||
tenantid: ZTenantId,
|
||||
conf: &'static PageServerConf,
|
||||
data_checksums: bool,
|
||||
|
||||
process: Mutex<Option<PostgresRedoProcess>>,
|
||||
}
|
||||
@@ -230,11 +231,16 @@ impl PostgresRedoManager {
|
||||
///
|
||||
/// Create a new PostgresRedoManager.
|
||||
///
|
||||
pub fn new(conf: &'static PageServerConf, tenantid: ZTenantId) -> PostgresRedoManager {
|
||||
pub fn new(
|
||||
conf: &'static PageServerConf,
|
||||
data_checksums: bool,
|
||||
tenantid: ZTenantId,
|
||||
) -> PostgresRedoManager {
|
||||
// The actual process is launched lazily, on first request.
|
||||
PostgresRedoManager {
|
||||
tenantid,
|
||||
conf,
|
||||
data_checksums,
|
||||
process: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
@@ -269,7 +275,13 @@ impl PostgresRedoManager {
|
||||
// Relational WAL records are applied using wal-redo-postgres
|
||||
let buf_tag = BufferTag { rel, blknum };
|
||||
let result = process
|
||||
.apply_wal_records(buf_tag, base_img, records, wal_redo_timeout)
|
||||
.apply_wal_records(
|
||||
buf_tag,
|
||||
base_img,
|
||||
records,
|
||||
wal_redo_timeout,
|
||||
self.data_checksums,
|
||||
)
|
||||
.map_err(WalRedoError::IoError);
|
||||
|
||||
let end_time = Instant::now();
|
||||
@@ -718,6 +730,7 @@ impl PostgresRedoProcess {
|
||||
base_img: Option<Bytes>,
|
||||
records: &[(Lsn, ZenithWalRecord)],
|
||||
wal_redo_timeout: Duration,
|
||||
data_checksums: bool,
|
||||
) -> Result<Bytes, std::io::Error> {
|
||||
// Serialize all the messages to send the WAL redo process first.
|
||||
//
|
||||
@@ -727,7 +740,9 @@ impl PostgresRedoProcess {
|
||||
let mut writebuf: Vec<u8> = Vec::new();
|
||||
build_begin_redo_for_block_msg(tag, &mut writebuf);
|
||||
if let Some(img) = base_img {
|
||||
if !page_verify_checksum(&img, tag.blknum) {
|
||||
// Checksums could be not stamped for old tenants, so check them only if they
|
||||
// are enabled (this is controlled by per-tenant config).
|
||||
if data_checksums && !page_verify_checksum(&img, tag.blknum) {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
format!("block {} of relation {} is invalid", tag.blknum, tag.rel),
|
||||
@@ -751,6 +766,8 @@ impl PostgresRedoProcess {
|
||||
),
|
||||
)
|
||||
})?;
|
||||
// WAL records always have a checksum, check it before sending to redo process.
|
||||
// It doesn't do these checks itself.
|
||||
if !wal_record_verify_checksum(&xlogrec, postgres_rec) {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidData,
|
||||
|
||||
Reference in New Issue
Block a user