Turn off data-checksums for old tenants by default and explicitly enable for new ones

This commit is contained in:
Alexey Kondratov
2022-07-06 13:57:55 +02:00
parent cc6ffb558d
commit 53b9cb915e
8 changed files with 54 additions and 9 deletions

View File

@@ -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
)
})

View File

@@ -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]

View File

@@ -494,6 +494,8 @@ components:
type: string
compaction_threshold:
type: string
data_checksums:
type: boolean
TenantConfigInfo:
type: object
properties:

View File

@@ -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)?);

View File

@@ -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),
}
}
}

View File

@@ -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,
}
}
}

View File

@@ -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))

View File

@@ -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,