blob: d63cc541e67005203d6e3fd873972d443e416970 [file]
mod converter;
mod persistency;
use crate::compat::storage_conversion::persistency::{personal_access_tokens, streams, users};
use crate::configs::system::SystemConfig;
use crate::state::system::{PartitionState, StreamState, TopicState};
use crate::state::State;
use crate::streaming::batching::message_batch::RetainedMessageBatch;
use crate::streaming::partitions::partition::{ConsumerOffset, Partition};
use crate::streaming::persistence::persister::Persister;
use crate::streaming::segments::index::{Index, IndexRange};
use crate::streaming::segments::segment::Segment;
use crate::streaming::segments::time_index::TimeIndex;
use crate::streaming::storage::{
PartitionStorage, SegmentStorage, StreamStorage, SystemInfoStorage, SystemStorage, TopicStorage,
};
use crate::streaming::streams::stream::Stream;
use crate::streaming::systems::info::SystemInfo;
use crate::streaming::topics::topic::Topic;
use async_trait::async_trait;
use iggy::consumer::ConsumerKind;
use iggy::error::IggyError;
use std::path::Path;
use std::sync::Arc;
use tokio::fs::{read_dir, rename};
use tracing::{error, info};
pub async fn init(
config: Arc<SystemConfig>,
metadata: Arc<dyn State>,
storage: Arc<SystemStorage>,
) -> Result<(), IggyError> {
if Path::new(&config.get_state_log_path()).exists() {
info!("State log already exists, skipping storage migration");
return Ok(());
}
let path = config.get_database_path();
if path.is_none() {
info!("No database path configured, skipping storage migration");
return Ok(());
}
let database_path = path.unwrap();
if !Path::new(&database_path).exists() {
error!("Database directory: {database_path} does not exist - cannot migrate storage.");
return Err(IggyError::CannotOpenDatabase(database_path));
}
let db_file = format!("{database_path}/db");
if !Path::new(&db_file).exists() {
error!("Database file at path: {db_file} does not exist - cannot migrate storage.");
return Err(IggyError::CannotOpenDatabase(db_file));
}
info!("Starting storage migration, database path: {database_path}");
let db = sled::open(&database_path);
if db.is_err() {
panic!("Cannot open database at: {database_path}");
}
let db = db.unwrap();
let mut streams = Vec::new();
let dir_entries = read_dir(&config.get_streams_path()).await;
if let Err(error) = dir_entries {
error!("Cannot read streams directory: {}", error);
return Err(IggyError::CannotReadStreams);
}
let noop_storage = SystemStorage {
info: Arc::new(NoopSystemInfoStorage {}),
stream: Arc::new(NoopStreamStorage {}),
topic: Arc::new(NoopTopicStorage {}),
partition: Arc::new(NoopPartitionStorage {}),
segment: Arc::new(NoopSegmentStorage {}),
persister: Arc::new(NoopPersister {}),
};
let noop_storage = Arc::new(noop_storage);
let mut dir_entries = dir_entries.unwrap();
while let Some(dir_entry) = dir_entries.next_entry().await.unwrap_or(None) {
let name = dir_entry.file_name().into_string().unwrap();
let stream_id = name.parse::<u32>();
if stream_id.is_err() {
error!("Invalid stream ID file with name: '{}'.", name);
continue;
}
let stream_id = stream_id.unwrap();
let mut stream = Stream::empty(stream_id, "stream", config.clone(), noop_storage.clone());
streams::load(&config, &db, &mut stream).await?;
streams.push(stream);
}
let users = users::load_all(&db).await?;
let personal_access_tokens = personal_access_tokens::load_all(&db).await?;
converter::convert(metadata, storage, streams, users, personal_access_tokens).await?;
let old_database_path = format!("{database_path}_old");
rename(&database_path, &old_database_path).await?;
info!("Storage migration has completed, new state log was cacreated and old database was moved to: {old_database_path} (now it can be safely deleted).");
Ok(())
}
struct NoopPersister {}
struct NoopSystemInfoStorage {}
struct NoopStreamStorage {}
struct NoopTopicStorage {}
struct NoopPartitionStorage {}
struct NoopSegmentStorage {}
#[async_trait]
impl Persister for NoopPersister {
async fn append(&self, _path: &str, _bytes: &[u8]) -> Result<(), IggyError> {
Ok(())
}
async fn overwrite(&self, _path: &str, _bytes: &[u8]) -> Result<(), IggyError> {
Ok(())
}
async fn delete(&self, _path: &str) -> Result<(), IggyError> {
Ok(())
}
}
#[async_trait]
impl SystemInfoStorage for NoopSystemInfoStorage {
async fn load(&self) -> Result<SystemInfo, IggyError> {
Ok(SystemInfo::default())
}
async fn save(&self, _system_info: &SystemInfo) -> Result<(), IggyError> {
Ok(())
}
}
#[async_trait]
impl StreamStorage for NoopStreamStorage {
async fn load(&self, _stream: &mut Stream, _state: StreamState) -> Result<(), IggyError> {
Ok(())
}
async fn save(&self, _stream: &Stream) -> Result<(), IggyError> {
Ok(())
}
async fn delete(&self, _stream: &Stream) -> Result<(), IggyError> {
Ok(())
}
}
#[async_trait]
impl TopicStorage for NoopTopicStorage {
async fn load(&self, _topic: &mut Topic, _state: TopicState) -> Result<(), IggyError> {
Ok(())
}
async fn save(&self, _topic: &Topic) -> Result<(), IggyError> {
Ok(())
}
async fn delete(&self, _topic: &Topic) -> Result<(), IggyError> {
Ok(())
}
}
#[async_trait]
impl PartitionStorage for NoopPartitionStorage {
async fn load(
&self,
_partition: &mut Partition,
_state: PartitionState,
) -> Result<(), IggyError> {
Ok(())
}
async fn save(&self, _partition: &Partition) -> Result<(), IggyError> {
Ok(())
}
async fn delete(&self, _partition: &Partition) -> Result<(), IggyError> {
Ok(())
}
async fn save_consumer_offset(&self, _offset: &ConsumerOffset) -> Result<(), IggyError> {
Ok(())
}
async fn load_consumer_offsets(
&self,
_kind: ConsumerKind,
_path: &str,
) -> Result<Vec<ConsumerOffset>, IggyError> {
Ok(vec![])
}
async fn delete_consumer_offsets(&self, _path: &str) -> Result<(), IggyError> {
Ok(())
}
async fn delete_consumer_offset(&self, _path: &str) -> Result<(), IggyError> {
Ok(())
}
}
#[async_trait]
impl SegmentStorage for NoopSegmentStorage {
async fn load(&self, _segment: &mut Segment) -> Result<(), IggyError> {
Ok(())
}
async fn save(&self, _segment: &Segment) -> Result<(), IggyError> {
Ok(())
}
async fn delete(&self, _segment: &Segment) -> Result<(), IggyError> {
Ok(())
}
async fn load_message_batches(
&self,
_segment: &Segment,
_index_range: &IndexRange,
) -> Result<Vec<RetainedMessageBatch>, IggyError> {
Ok(vec![])
}
async fn load_newest_batches_by_size(
&self,
_segment: &Segment,
_size: u64,
) -> Result<Vec<RetainedMessageBatch>, IggyError> {
Ok(vec![])
}
async fn save_batches(
&self,
_segment: &Segment,
_batches: &[Arc<RetainedMessageBatch>],
) -> Result<u32, IggyError> {
Ok(0)
}
async fn load_message_ids(&self, _segment: &Segment) -> Result<Vec<u128>, IggyError> {
Ok(vec![])
}
async fn load_checksums(&self, _segment: &Segment) -> Result<(), IggyError> {
Ok(())
}
async fn load_all_indexes(&self, _segment: &Segment) -> Result<Vec<Index>, IggyError> {
Ok(vec![])
}
async fn load_index_range(
&self,
_segment: &Segment,
_index_start_offset: u64,
_index_end_offset: u64,
) -> Result<Option<IndexRange>, IggyError> {
Ok(None)
}
async fn save_index(&self, _segment: &Segment) -> Result<(), IggyError> {
Ok(())
}
async fn try_load_time_index_for_timestamp(
&self,
_segment: &Segment,
_timestamp: u64,
) -> Result<Option<TimeIndex>, IggyError> {
Ok(None)
}
async fn load_all_time_indexes(&self, _segment: &Segment) -> Result<Vec<TimeIndex>, IggyError> {
Ok(vec![])
}
async fn load_last_time_index(
&self,
_segment: &Segment,
) -> Result<Option<TimeIndex>, IggyError> {
Ok(None)
}
async fn save_time_index(&self, _segment: &Segment) -> Result<(), IggyError> {
Ok(())
}
}