refactor: Move the database rules functionality to iox_object_store
parent
4447f1e22c
commit
63111d9d9a
|
@ -14,9 +14,12 @@
|
||||||
// TODO: Create an IoxPath type and only take/return paths of those types, and wrap in the
|
// TODO: Create an IoxPath type and only take/return paths of those types, and wrap in the
|
||||||
// database's root path before sending to the underlying object_store.
|
// database's root path before sending to the underlying object_store.
|
||||||
|
|
||||||
use bytes::Bytes;
|
use bytes::{Bytes, BytesMut};
|
||||||
use data_types::{server_id::ServerId, DatabaseName};
|
use data_types::{server_id::ServerId, DatabaseName};
|
||||||
use futures::{stream::BoxStream, Stream, StreamExt, TryStreamExt};
|
use futures::{
|
||||||
|
stream::{self, BoxStream},
|
||||||
|
Stream, StreamExt, TryStreamExt,
|
||||||
|
};
|
||||||
use object_store::{
|
use object_store::{
|
||||||
path::{parsed::DirsAndFileName, ObjectStorePath, Path},
|
path::{parsed::DirsAndFileName, ObjectStorePath, Path},
|
||||||
ObjectStore, ObjectStoreApi, Result,
|
ObjectStore, ObjectStoreApi, Result,
|
||||||
|
@ -29,6 +32,8 @@ mod paths;
|
||||||
use paths::{DataPath, RootPath};
|
use paths::{DataPath, RootPath};
|
||||||
pub use paths::{ParquetFilePath, ParquetFilePathParseError};
|
pub use paths::{ParquetFilePath, ParquetFilePathParseError};
|
||||||
|
|
||||||
|
const DB_RULES_FILE_NAME: &str = "rules.pb";
|
||||||
|
|
||||||
/// Handles persistence of data for a particular database. Writes within its directory/prefix.
|
/// Handles persistence of data for a particular database. Writes within its directory/prefix.
|
||||||
///
|
///
|
||||||
/// This wrapper on top of an `ObjectStore` maps IOx specific concepts to ObjectStore locations
|
/// This wrapper on top of an `ObjectStore` maps IOx specific concepts to ObjectStore locations
|
||||||
|
@ -141,6 +146,37 @@ impl IoxObjectStore {
|
||||||
self.inner.delete(&full_path).await
|
self.inner.delete(&full_path).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn db_rules_path(&self) -> Path {
|
||||||
|
self.root_path.join(DB_RULES_FILE_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the data for the database rules
|
||||||
|
pub async fn get_database_rules_file(&self) -> Result<bytes::Bytes> {
|
||||||
|
let mut stream = self.inner.get(&self.db_rules_path()).await?;
|
||||||
|
let mut bytes = BytesMut::new();
|
||||||
|
|
||||||
|
while let Some(buf) = stream.next().await {
|
||||||
|
bytes.extend(buf?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(bytes.freeze())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Store the data for the database rules
|
||||||
|
pub async fn put_database_rules_file(&self, bytes: bytes::Bytes) -> Result<()> {
|
||||||
|
let len = bytes.len();
|
||||||
|
let stream = stream::once(async move { Ok(bytes) });
|
||||||
|
|
||||||
|
self.inner
|
||||||
|
.put(&self.db_rules_path(), stream, Some(len))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete the data for the database rules
|
||||||
|
pub async fn delete_database_rules_file(&self) -> Result<()> {
|
||||||
|
self.inner.delete(&self.db_rules_path()).await
|
||||||
|
}
|
||||||
|
|
||||||
/// List the relative paths in this database's object store.
|
/// List the relative paths in this database's object store.
|
||||||
pub async fn list(
|
pub async fn list(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::{
|
||||||
load::{create_preserved_catalog, load_or_create_preserved_catalog},
|
load::{create_preserved_catalog, load_or_create_preserved_catalog},
|
||||||
DatabaseToCommit,
|
DatabaseToCommit,
|
||||||
},
|
},
|
||||||
ApplicationState, Db, DB_RULES_FILE_NAME,
|
ApplicationState, Db,
|
||||||
};
|
};
|
||||||
use bytes::BytesMut;
|
use bytes::BytesMut;
|
||||||
use data_types::{
|
use data_types::{
|
||||||
|
@ -17,10 +17,7 @@ use futures::{
|
||||||
use generated_types::database_rules::encode_database_rules;
|
use generated_types::database_rules::encode_database_rules;
|
||||||
use internal_types::freezable::Freezable;
|
use internal_types::freezable::Freezable;
|
||||||
use iox_object_store::IoxObjectStore;
|
use iox_object_store::IoxObjectStore;
|
||||||
use object_store::{
|
use object_store::path::Path;
|
||||||
path::{ObjectStorePath, Path},
|
|
||||||
ObjectStore, ObjectStoreApi,
|
|
||||||
};
|
|
||||||
use observability_deps::tracing::{error, info, warn};
|
use observability_deps::tracing::{error, info, warn};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use parquet_file::catalog::PreservedCatalog;
|
use parquet_file::catalog::PreservedCatalog;
|
||||||
|
@ -129,21 +126,18 @@ impl Database {
|
||||||
/// Create fresh database w/o any state.
|
/// Create fresh database w/o any state.
|
||||||
pub async fn create(
|
pub async fn create(
|
||||||
application: Arc<ApplicationState>,
|
application: Arc<ApplicationState>,
|
||||||
store_prefix: &Path,
|
|
||||||
rules: DatabaseRules,
|
rules: DatabaseRules,
|
||||||
server_id: ServerId,
|
server_id: ServerId,
|
||||||
) -> Result<(), InitError> {
|
) -> Result<(), InitError> {
|
||||||
let db_name = rules.name.clone();
|
let db_name = rules.name.clone();
|
||||||
|
let object_store =
|
||||||
|
IoxObjectStore::new(Arc::clone(application.object_store()), server_id, &db_name);
|
||||||
|
|
||||||
persist_database_rules(application.object_store(), store_prefix, rules).await?;
|
persist_database_rules(&object_store, rules).await?;
|
||||||
|
|
||||||
create_preserved_catalog(
|
create_preserved_catalog(
|
||||||
db_name.as_str(),
|
db_name.as_str(),
|
||||||
Arc::new(IoxObjectStore::new(
|
Arc::new(object_store),
|
||||||
Arc::clone(application.object_store()),
|
|
||||||
server_id,
|
|
||||||
&db_name,
|
|
||||||
)),
|
|
||||||
server_id,
|
server_id,
|
||||||
Arc::clone(application.metric_registry()),
|
Arc::clone(application.metric_registry()),
|
||||||
true,
|
true,
|
||||||
|
@ -608,11 +602,10 @@ impl DatabaseStateKnown {
|
||||||
&self,
|
&self,
|
||||||
shared: &DatabaseShared,
|
shared: &DatabaseShared,
|
||||||
) -> Result<DatabaseStateRulesLoaded, InitError> {
|
) -> Result<DatabaseStateRulesLoaded, InitError> {
|
||||||
let mut location = shared.config.store_prefix.clone();
|
|
||||||
location.set_file_name(DB_RULES_FILE_NAME);
|
|
||||||
|
|
||||||
// TODO: Retry this
|
// TODO: Retry this
|
||||||
let bytes = get_store_bytes(shared.application.object_store().as_ref(), &location)
|
let bytes = shared
|
||||||
|
.iox_object_store
|
||||||
|
.get_database_rules_file()
|
||||||
.await
|
.await
|
||||||
.context(RulesFetch)?;
|
.context(RulesFetch)?;
|
||||||
|
|
||||||
|
@ -715,47 +708,16 @@ struct DatabaseStateInitialized {
|
||||||
db: Arc<Db>,
|
db: Arc<Db>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the bytes for a given object store location
|
|
||||||
///
|
|
||||||
/// TODO: move to object_store crate
|
|
||||||
async fn get_store_bytes(
|
|
||||||
store: &ObjectStore,
|
|
||||||
location: &Path,
|
|
||||||
) -> Result<bytes::Bytes, object_store::Error> {
|
|
||||||
use futures::stream::TryStreamExt;
|
|
||||||
|
|
||||||
let stream = store.get(location).await?;
|
|
||||||
let bytes = stream
|
|
||||||
.try_fold(BytesMut::new(), |mut acc, buf| async move {
|
|
||||||
acc.extend_from_slice(&buf);
|
|
||||||
Ok(acc)
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(bytes.freeze())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Persist the the `DatabaseRules` given the `Database` store prefix
|
/// Persist the the `DatabaseRules` given the `Database` store prefix
|
||||||
pub(super) async fn persist_database_rules(
|
pub(super) async fn persist_database_rules(
|
||||||
object_store: &ObjectStore,
|
object_store: &IoxObjectStore,
|
||||||
store_prefix: &Path,
|
|
||||||
rules: DatabaseRules,
|
rules: DatabaseRules,
|
||||||
) -> Result<(), InitError> {
|
) -> Result<(), InitError> {
|
||||||
let mut data = BytesMut::new();
|
let mut data = BytesMut::new();
|
||||||
encode_database_rules(rules, &mut data).context(ErrorSerializingRulesProtobuf)?;
|
encode_database_rules(rules, &mut data).context(ErrorSerializingRulesProtobuf)?;
|
||||||
|
|
||||||
let mut location = store_prefix.clone();
|
|
||||||
location.set_file_name(DB_RULES_FILE_NAME);
|
|
||||||
|
|
||||||
let len = data.len();
|
|
||||||
|
|
||||||
let stream_data = std::io::Result::Ok(data.freeze());
|
|
||||||
object_store
|
object_store
|
||||||
.put(
|
.put_database_rules_file(data.freeze())
|
||||||
&location,
|
|
||||||
futures::stream::once(async move { stream_data }),
|
|
||||||
Some(len),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
.context(StoreError)?;
|
.context(StoreError)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -766,6 +728,7 @@ mod tests {
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use data_types::database_rules::{PartitionTemplate, TemplatePart, WriteBufferConnection};
|
use data_types::database_rules::{PartitionTemplate, TemplatePart, WriteBufferConnection};
|
||||||
use entry::{test_helpers::lp_to_entries, Sequence, SequencedEntry};
|
use entry::{test_helpers::lp_to_entries, Sequence, SequencedEntry};
|
||||||
|
use object_store::{ObjectStore, ObjectStoreApi};
|
||||||
use write_buffer::{config::WriteBufferConfigFactory, mock::MockBufferSharedState};
|
use write_buffer::{config::WriteBufferConfigFactory, mock::MockBufferSharedState};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -877,7 +840,7 @@ mod tests {
|
||||||
)),
|
)),
|
||||||
};
|
};
|
||||||
let store_prefix = application.object_store().new_path();
|
let store_prefix = application.object_store().new_path();
|
||||||
Database::create(Arc::clone(&application), &store_prefix, rules, server_id)
|
Database::create(Arc::clone(&application), rules, server_id)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let db_config = DatabaseConfig {
|
let db_config = DatabaseConfig {
|
||||||
|
|
|
@ -689,14 +689,9 @@ where
|
||||||
};
|
};
|
||||||
|
|
||||||
let store_prefix = database_store_prefix(object_store, server_id, &db_name);
|
let store_prefix = database_store_prefix(object_store, server_id, &db_name);
|
||||||
Database::create(
|
Database::create(Arc::clone(&self.shared.application), rules, server_id)
|
||||||
Arc::clone(&self.shared.application),
|
.await
|
||||||
&store_prefix,
|
.context(CannotCreateDatabase)?;
|
||||||
rules,
|
|
||||||
server_id,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(|e| Error::CannotCreateDatabase { source: e })?;
|
|
||||||
|
|
||||||
let database = {
|
let database = {
|
||||||
let mut state = self.shared.state.write();
|
let mut state = self.shared.state.write();
|
||||||
|
@ -964,13 +959,9 @@ where
|
||||||
let rules = db.update_rules(update).map_err(UpdateError::Closure)?;
|
let rules = db.update_rules(update).map_err(UpdateError::Closure)?;
|
||||||
|
|
||||||
// TODO: Handle failure
|
// TODO: Handle failure
|
||||||
persist_database_rules(
|
persist_database_rules(&database.iox_object_store(), rules.as_ref().clone())
|
||||||
self.shared.application.object_store().as_ref(),
|
.await
|
||||||
&database.config().store_prefix,
|
.context(CannotPersistUpdatedRules)?;
|
||||||
rules.as_ref().clone(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.map_err(|e| Error::CannotPersistUpdatedRules { source: e })?;
|
|
||||||
Ok(rules)
|
Ok(rules)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1226,8 +1217,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) const DB_RULES_FILE_NAME: &str = "rules.pb";
|
|
||||||
|
|
||||||
/// Returns a list of database names and their prefix in object storage
|
/// Returns a list of database names and their prefix in object storage
|
||||||
async fn list_databases(
|
async fn list_databases(
|
||||||
object_store: &ObjectStore,
|
object_store: &ObjectStore,
|
||||||
|
@ -1284,12 +1273,11 @@ mod tests {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use entry::test_helpers::lp_to_entry;
|
use entry::test_helpers::lp_to_entry;
|
||||||
use futures::TryStreamExt;
|
|
||||||
use generated_types::database_rules::decode_database_rules;
|
use generated_types::database_rules::decode_database_rules;
|
||||||
use influxdb_line_protocol::parse_lines;
|
use influxdb_line_protocol::parse_lines;
|
||||||
use iox_object_store::IoxObjectStore;
|
use iox_object_store::IoxObjectStore;
|
||||||
use metrics::TestMetricRegistry;
|
use metrics::TestMetricRegistry;
|
||||||
use object_store::{path::ObjectStorePath, ObjectStore};
|
use object_store::ObjectStore;
|
||||||
use parquet_file::catalog::{test_helpers::TestCatalogState, PreservedCatalog};
|
use parquet_file::catalog::{test_helpers::TestCatalogState, PreservedCatalog};
|
||||||
use query::{exec::ExecutorType, frontend::sql::SqlQueryPlanner, QueryDatabase};
|
use query::{exec::ExecutorType, frontend::sql::SqlQueryPlanner, QueryDatabase};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -1360,26 +1348,16 @@ mod tests {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a database
|
// Create a database
|
||||||
server
|
let bananas = server
|
||||||
.create_database(rules.clone())
|
.create_database(rules.clone())
|
||||||
.await
|
.await
|
||||||
.expect("failed to create database");
|
.expect("failed to create database");
|
||||||
|
|
||||||
let mut rules_path = application.object_store().new_path();
|
let read_data = bananas
|
||||||
rules_path.push_all_dirs(&["1", name.as_str()]);
|
.iox_object_store()
|
||||||
rules_path.set_file_name("rules.pb");
|
.get_database_rules_file()
|
||||||
|
|
||||||
let read_data = application
|
|
||||||
.object_store()
|
|
||||||
.get(&rules_path)
|
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap();
|
||||||
.map_ok(|b| bytes::BytesMut::from(&b[..]))
|
|
||||||
.try_concat()
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.freeze();
|
|
||||||
|
|
||||||
let read_rules = decode_database_rules(read_data).unwrap();
|
let read_rules = decode_database_rules(read_data).unwrap();
|
||||||
|
|
||||||
assert_eq!(rules, read_rules);
|
assert_eq!(rules, read_rules);
|
||||||
|
@ -1464,19 +1442,14 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn load_databases() {
|
async fn load_databases() {
|
||||||
let application = make_application();
|
let application = make_application();
|
||||||
let store = application.object_store();
|
|
||||||
|
|
||||||
let server = make_server(Arc::clone(&application));
|
let server = make_server(Arc::clone(&application));
|
||||||
server.set_id(ServerId::try_from(1).unwrap()).unwrap();
|
server.set_id(ServerId::try_from(1).unwrap()).unwrap();
|
||||||
server.wait_for_init().await.unwrap();
|
server.wait_for_init().await.unwrap();
|
||||||
create_simple_database(&server, "bananas")
|
let bananas = create_simple_database(&server, "bananas")
|
||||||
.await
|
.await
|
||||||
.expect("failed to create database");
|
.expect("failed to create database");
|
||||||
|
|
||||||
let mut rules_path = store.new_path();
|
|
||||||
rules_path.push_all_dirs(&["1", "bananas"]);
|
|
||||||
rules_path.set_file_name("rules.pb");
|
|
||||||
|
|
||||||
std::mem::drop(server);
|
std::mem::drop(server);
|
||||||
|
|
||||||
let server = make_server(Arc::clone(&application));
|
let server = make_server(Arc::clone(&application));
|
||||||
|
@ -1491,8 +1464,9 @@ mod tests {
|
||||||
|
|
||||||
std::mem::drop(server);
|
std::mem::drop(server);
|
||||||
|
|
||||||
store
|
bananas
|
||||||
.delete(&rules_path)
|
.iox_object_store()
|
||||||
|
.delete_database_rules_file()
|
||||||
.await
|
.await
|
||||||
.expect("cannot delete rules file");
|
.expect("cannot delete rules file");
|
||||||
|
|
||||||
|
@ -1941,21 +1915,12 @@ mod tests {
|
||||||
.expect("failed to create database");
|
.expect("failed to create database");
|
||||||
|
|
||||||
// tamper store
|
// tamper store
|
||||||
let mut path = database_store_prefix(store.as_ref(), server_id, &bar_db_name);
|
let iox_object_store = IoxObjectStore::new(store, server_id, &bar_db_name);
|
||||||
path.set_file_name(DB_RULES_FILE_NAME);
|
iox_object_store
|
||||||
|
.put_database_rules_file(Bytes::from("x"))
|
||||||
let data = Bytes::from("x");
|
|
||||||
let len = data.len();
|
|
||||||
store
|
|
||||||
.put(
|
|
||||||
&path,
|
|
||||||
futures::stream::once(async move { Ok(data) }),
|
|
||||||
Some(len),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
iox_object_store.get_database_rules_file().await.unwrap();
|
||||||
store.get(&path).await.unwrap();
|
|
||||||
|
|
||||||
// start server
|
// start server
|
||||||
let server = make_server(application);
|
let server = make_server(application);
|
||||||
|
@ -2021,7 +1986,6 @@ mod tests {
|
||||||
|
|
||||||
// setup
|
// setup
|
||||||
let application = make_application();
|
let application = make_application();
|
||||||
let store = Arc::clone(application.object_store());
|
|
||||||
let server_id = ServerId::try_from(1).unwrap();
|
let server_id = ServerId::try_from(1).unwrap();
|
||||||
|
|
||||||
// Create temporary server to create existing databases
|
// Create temporary server to create existing databases
|
||||||
|
@ -2033,7 +1997,7 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.expect("failed to create database");
|
.expect("failed to create database");
|
||||||
|
|
||||||
create_simple_database(&server, db_name_rules_broken.clone())
|
let rules_broken = create_simple_database(&server, db_name_rules_broken.clone())
|
||||||
.await
|
.await
|
||||||
.expect("failed to create database");
|
.expect("failed to create database");
|
||||||
|
|
||||||
|
@ -2042,18 +2006,9 @@ mod tests {
|
||||||
.expect("failed to create database");
|
.expect("failed to create database");
|
||||||
|
|
||||||
// tamper store to break one database
|
// tamper store to break one database
|
||||||
let mut path = database_store_prefix(store.as_ref(), server_id, &db_name_rules_broken);
|
rules_broken
|
||||||
path.set_file_name(DB_RULES_FILE_NAME);
|
.iox_object_store()
|
||||||
|
.put_database_rules_file(Bytes::from("x"))
|
||||||
let data = Bytes::from("x");
|
|
||||||
let len = data.len();
|
|
||||||
|
|
||||||
store
|
|
||||||
.put(
|
|
||||||
&path,
|
|
||||||
futures::stream::once(async move { Ok(data) }),
|
|
||||||
Some(len),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -2067,7 +2022,11 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
drop(preserved_catalog);
|
drop(preserved_catalog);
|
||||||
|
|
||||||
store.get(&path).await.unwrap();
|
rules_broken
|
||||||
|
.iox_object_store()
|
||||||
|
.get_database_rules_file()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// boot actual test server
|
// boot actual test server
|
||||||
let server = make_server(Arc::clone(&application));
|
let server = make_server(Arc::clone(&application));
|
||||||
|
|
Loading…
Reference in New Issue