use std::sync::Arc;
use tracing::info;
use crate::ServerConfig;
use crate::bridge::dispatch::{CoreChannelDataSide, Dispatcher};
use crate::bridge::quiesce::CollectionQuiesce;
use crate::control::array_catalog::ArrayCatalog;
use crate::control::metrics::SystemMetrics;
use crate::control::server::shared::ddl::neutral::collection::register::{
build_doc_config_from_stored, derive_auto_indexes, extend_with_catalog_indexes,
};
use crate::data::eventfd::EventFdNotifier;
use crate::data::runtime::{CoreCompactionConfig, SpawnCoreParams, spawn_core};
use crate::event::EventProducer;
use crate::storage::quarantine::QuarantineRegistry;
use crate::types::{DatabaseId, TenantId};
pub fn load_array_catalog(
config: &ServerConfig,
) -> crate::control::array_catalog::ArrayCatalogHandle {
let array_catalog = ArrayCatalog::handle();
let catalog_path = config.catalog_path();
match crate::control::security::catalog::SystemCatalog::open(&catalog_path) {
Ok(catalog) => match catalog.load_all_arrays() {
Ok(entries) => {
let mut guard = array_catalog
.write()
.expect("array catalog lock poisoned at startup");
for entry in entries {
if let Err(e) = guard.register(entry) {
tracing::warn!(error = %e, "failed to register array at startup");
}
}
}
Err(e) => {
tracing::warn!(error = %e, "failed to load _system.arrays at startup");
}
},
Err(e) => {
tracing::warn!(error = %e, "could not open system catalog to load arrays");
}
}
array_catalog
}
pub fn load_doc_config_registry(
config: &ServerConfig,
) -> Vec<crate::data::executor::core_loop::DocConfigSeedEntry> {
let catalog_path = config.catalog_path();
let catalog = match crate::control::security::catalog::SystemCatalog::open(&catalog_path) {
Ok(catalog) => catalog,
Err(e) => {
tracing::warn!(error = %e, "could not open system catalog to seed doc_configs");
return Vec::new();
}
};
let all = match crate::bootstrap::constraint_reconcile::load_collections(&catalog) {
Ok(all) => all,
Err(e) => {
tracing::warn!(error = %e, "failed to load collections to seed doc_configs");
return Vec::new();
}
};
all.into_iter()
.filter(|(_, coll)| coll.is_active)
.map(|(database_id, coll)| {
let tenant_id = TenantId::new(coll.tenant_id);
let mut indexes = derive_auto_indexes(coll.fields.iter().map(|(n, _)| n.as_str()));
extend_with_catalog_indexes(&mut indexes, &coll);
let config = build_doc_config_from_stored(&catalog, tenant_id, &coll, &indexes);
let key = (database_id, tenant_id, config.name.clone());
(key, config)
})
.collect()
}
pub fn load_vector_index_param_seed(
config: &ServerConfig,
) -> Vec<nodedb_types::StoredVectorIndexParams> {
let catalog_path = config.catalog_path();
let catalog = match crate::control::security::catalog::SystemCatalog::open(&catalog_path) {
Ok(catalog) => catalog,
Err(e) => {
tracing::warn!(error = %e, "could not open system catalog to seed vector index params");
return Vec::new();
}
};
match catalog.list_all_vector_index_params() {
Ok(entries) => entries,
Err(e) => {
tracing::warn!(error = %e, "failed to load vector index params to seed cores");
Vec::new()
}
}
}
pub fn load_columnar_schema_seed(
config: &ServerConfig,
) -> Vec<(
DatabaseId,
TenantId,
String,
nodedb_types::columnar::ColumnarSchema,
)> {
let catalog_path = config.catalog_path();
let catalog = match crate::control::security::catalog::SystemCatalog::open(&catalog_path) {
Ok(catalog) => catalog,
Err(e) => {
tracing::warn!(error = %e, "could not open system catalog to seed columnar schemas");
return Vec::new();
}
};
let all = match crate::bootstrap::constraint_reconcile::load_collections(&catalog) {
Ok(all) => all,
Err(e) => {
tracing::warn!(error = %e, "failed to load collections to seed columnar schemas");
return Vec::new();
}
};
all.into_iter()
.filter(|(_, coll)| coll.is_active && coll.collection_type.is_columnar_family())
.filter_map(|(database_id, coll)| {
let schema = crate::control::planner::sql_plan_convert::dml::build_columnar_schema(
&coll.fields,
)?;
Some((
database_id,
TenantId::new(coll.tenant_id),
coll.name.clone(),
schema,
))
})
.collect()
}
pub struct SpawnedDataPlaneCores {
pub handles: Vec<std::thread::JoinHandle<()>>,
pub replay_done: Vec<tokio::sync::oneshot::Receiver<()>>,
}
pub struct CoreSharedResources {
pub governor: Arc<nodedb_mem::MemoryGovernor>,
pub quiesce: Arc<CollectionQuiesce>,
pub hlc: Arc<nodedb_types::OrdinalClock>,
pub array_catalog: crate::control::array_catalog::ArrayCatalogHandle,
pub quarantine_registry: Arc<QuarantineRegistry>,
pub system_metrics: Arc<SystemMetrics>,
pub maintenance_budget: Arc<crate::control::maintenance::MaintenanceBudgetTracker>,
pub doc_config_seed: Arc<Vec<crate::data::executor::core_loop::DocConfigSeedEntry>>,
pub vector_index_param_seed: Arc<Vec<nodedb_types::StoredVectorIndexParams>>,
pub columnar_schema_seed: Arc<
Vec<(
DatabaseId,
TenantId,
String,
nodedb_types::columnar::ColumnarSchema,
)>,
>,
}
pub fn spawn_data_plane_cores(
config: &ServerConfig,
data_sides: Vec<CoreChannelDataSide>,
event_producers: Vec<EventProducer>,
wal_records: Arc<[nodedb_wal::WalRecord]>,
replay_tombstones: nodedb_wal::TombstoneSet,
dispatcher: &mut Dispatcher,
resources: CoreSharedResources,
) -> anyhow::Result<SpawnedDataPlaneCores> {
let CoreSharedResources {
governor,
quiesce,
hlc,
array_catalog,
quarantine_registry,
system_metrics,
maintenance_budget,
doc_config_seed,
vector_index_param_seed,
columnar_schema_seed,
} = resources;
let num_cores = config.server.data_plane_cores;
let compaction_cfg = CoreCompactionConfig {
interval: config.checkpoint.compaction_interval(),
tombstone_threshold: config.checkpoint.compaction_tombstone_threshold,
query: config.tuning.query.clone(),
graph: config.tuning.graph.clone(),
timeseries: config.tuning.timeseries.clone(),
checkpoint_interval: std::time::Duration::from_secs(config.checkpoint.interval_secs),
};
let mut core_handles = Vec::with_capacity(num_cores);
let mut replay_done_rxs = Vec::with_capacity(num_cores);
let mut notifiers: Vec<(usize, EventFdNotifier)> = Vec::with_capacity(num_cores);
for (core_id, (data_side, event_producer)) in
data_sides.into_iter().zip(event_producers).enumerate()
{
let (replay_done_tx, replay_done_rx) = tokio::sync::oneshot::channel();
let (handle, notifier) = spawn_core(SpawnCoreParams {
core_id,
request_rx: data_side.request_rx,
response_tx: data_side.response_tx,
data_dir: &config.server.data_dir,
wal_records: Arc::clone(&wal_records),
tombstones: replay_tombstones.clone(),
num_cores,
compaction_config: compaction_cfg.clone(),
system_metrics: Some(Arc::clone(&system_metrics)),
event_producer: Some(event_producer),
governor: Arc::clone(&governor),
quiesce: Some(Arc::clone(&quiesce)),
hlc: Arc::clone(&hlc),
array_catalog: Arc::clone(&array_catalog),
quarantine_registry: Arc::clone(&quarantine_registry),
maintenance_budget: Arc::clone(&maintenance_budget),
doc_config_seed: Arc::clone(&doc_config_seed),
vector_index_param_seed: Arc::clone(&vector_index_param_seed),
columnar_schema_seed: Arc::clone(&columnar_schema_seed),
replay_done: replay_done_tx,
})?;
core_handles.push(handle);
replay_done_rxs.push(replay_done_rx);
notifiers.push((core_id, notifier));
}
for (core_id, notifier) in ¬ifiers {
dispatcher.set_notifier(*core_id, *notifier);
}
info!(num_cores, "data plane cores running (eventfd-driven)");
Ok(SpawnedDataPlaneCores {
handles: core_handles,
replay_done: replay_done_rxs,
})
}