blob: b358ce4ac04ed80c94de3bbe45f320800e495cc2 [file]
use crate::streaming::session::Session;
use crate::streaming::streams::stream::Stream;
use crate::streaming::systems::system::System;
use futures::future::try_join_all;
use iggy::error::IggyError;
use iggy::identifier::{IdKind, Identifier};
use iggy::locking::IggySharedMutFn;
use iggy::utils::text;
use std::cell::RefCell;
use std::sync::atomic::{AtomicU32, Ordering};
use tokio::fs::read_dir;
use tracing::{error, info};
static CURRENT_STREAM_ID: AtomicU32 = AtomicU32::new(1);
impl System {
pub(crate) async fn load_streams(&mut self) -> Result<(), IggyError> {
info!("Loading streams from disk...");
let mut unloaded_streams = Vec::new();
let dir_entries = read_dir(&self.config.get_streams_path()).await;
if let Err(error) = dir_entries {
error!("Cannot read streams directory: {}", error);
return Err(IggyError::CannotReadStreams);
}
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 stream = Stream::empty(stream_id, self.config.clone(), self.storage.clone());
unloaded_streams.push(stream);
}
let loaded_streams = RefCell::new(Vec::new());
let load_stream_tasks = unloaded_streams.into_iter().map(|mut stream| {
let load_stream_task = async {
stream.load().await?;
loaded_streams.borrow_mut().push(stream);
Result::<(), IggyError>::Ok(())
};
load_stream_task
});
try_join_all(load_stream_tasks).await?;
for stream in loaded_streams.take() {
if self.streams.contains_key(&stream.stream_id) {
error!("Stream with ID: '{}' already exists.", &stream.stream_id);
continue;
}
if self.streams_ids.contains_key(&stream.name) {
error!("Stream with name: '{}' already exists.", &stream.name);
continue;
}
self.metrics.increment_streams(1);
self.metrics.increment_topics(stream.get_topics_count());
self.metrics
.increment_partitions(stream.get_partitions_count());
self.metrics
.increment_segments(stream.get_segments_count().await);
self.metrics.increment_messages(stream.get_messages_count());
self.streams_ids
.insert(stream.name.clone(), stream.stream_id);
self.streams.insert(stream.stream_id, stream);
}
info!("Loaded {} stream(s) from disk.", self.streams.len());
Ok(())
}
pub fn get_streams(&self) -> Vec<&Stream> {
self.streams.values().collect()
}
pub fn find_streams(&self, session: &Session) -> Result<Vec<&Stream>, IggyError> {
self.ensure_authenticated(session)?;
self.permissioner.get_streams(session.get_user_id())?;
Ok(self.get_streams())
}
pub fn find_stream(
&self,
session: &Session,
identifier: &Identifier,
) -> Result<&Stream, IggyError> {
self.ensure_authenticated(session)?;
let stream = self.get_stream(identifier)?;
self.permissioner
.get_stream(session.get_user_id(), stream.stream_id)?;
Ok(stream)
}
pub fn get_stream(&self, identifier: &Identifier) -> Result<&Stream, IggyError> {
match identifier.kind {
IdKind::Numeric => self.get_stream_by_id(identifier.get_u32_value()?),
IdKind::String => self.get_stream_by_name(&identifier.get_cow_str_value()?),
}
}
pub fn get_stream_mut(&mut self, identifier: &Identifier) -> Result<&mut Stream, IggyError> {
match identifier.kind {
IdKind::Numeric => self.get_stream_by_id_mut(identifier.get_u32_value()?),
IdKind::String => self.get_stream_by_name_mut(&identifier.get_cow_str_value()?),
}
}
fn get_stream_by_name(&self, name: &str) -> Result<&Stream, IggyError> {
let stream_id = self.streams_ids.get(name);
if stream_id.is_none() {
return Err(IggyError::StreamNameNotFound(name.to_string()));
}
self.get_stream_by_id(*stream_id.unwrap())
}
fn get_stream_by_id(&self, stream_id: u32) -> Result<&Stream, IggyError> {
let stream = self.streams.get(&stream_id);
if stream.is_none() {
return Err(IggyError::StreamIdNotFound(stream_id));
}
Ok(stream.unwrap())
}
fn get_stream_by_name_mut(&mut self, name: &str) -> Result<&mut Stream, IggyError> {
let stream_id;
{
let id = self.streams_ids.get_mut(name);
if id.is_none() {
return Err(IggyError::StreamNameNotFound(name.to_string()));
}
stream_id = *id.unwrap();
}
self.get_stream_by_id_mut(stream_id)
}
fn get_stream_by_id_mut(&mut self, stream_id: u32) -> Result<&mut Stream, IggyError> {
let stream = self.streams.get_mut(&stream_id);
if stream.is_none() {
return Err(IggyError::StreamIdNotFound(stream_id));
}
Ok(stream.unwrap())
}
pub async fn create_stream(
&mut self,
session: &Session,
stream_id: Option<u32>,
name: &str,
) -> Result<(), IggyError> {
self.ensure_authenticated(session)?;
self.permissioner.create_stream(session.get_user_id())?;
let name = text::to_lowercase_non_whitespace(name);
if self.streams_ids.contains_key(&name) {
return Err(IggyError::StreamNameAlreadyExists(name.to_string()));
}
let mut id;
if stream_id.is_none() {
id = CURRENT_STREAM_ID.fetch_add(1, Ordering::SeqCst);
loop {
if self.streams.contains_key(&id) {
if id == u32::MAX {
return Err(IggyError::StreamIdAlreadyExists(id));
}
id = CURRENT_STREAM_ID.fetch_add(1, Ordering::SeqCst);
} else {
break;
}
}
} else {
id = stream_id.unwrap();
}
if self.streams.contains_key(&id) {
return Err(IggyError::StreamIdAlreadyExists(id));
}
let stream = Stream::create(id, &name, self.config.clone(), self.storage.clone());
stream.persist().await?;
info!("Created stream with ID: {id}, name: '{name}'.");
self.streams_ids.insert(name, stream.stream_id);
self.streams.insert(stream.stream_id, stream);
self.metrics.increment_streams(1);
Ok(())
}
pub async fn update_stream(
&mut self,
session: &Session,
id: &Identifier,
name: &str,
) -> Result<(), IggyError> {
self.ensure_authenticated(session)?;
let stream_id;
{
let stream = self.get_stream(id)?;
stream_id = stream.stream_id;
}
self.permissioner
.update_stream(session.get_user_id(), stream_id)?;
let updated_name = text::to_lowercase_non_whitespace(name);
{
if let Some(stream_id_by_name) = self.streams_ids.get(&updated_name) {
if *stream_id_by_name != stream_id {
return Err(IggyError::StreamNameAlreadyExists(updated_name.clone()));
}
}
}
let old_name;
{
let stream = self.get_stream_mut(id)?;
old_name = stream.name.clone();
stream.name = updated_name.clone();
stream.persist().await?;
}
{
self.streams_ids.remove(&old_name);
self.streams_ids.insert(updated_name.clone(), stream_id);
}
info!(
"Stream with ID '{}' updated. Old name: '{}' changed to: '{}'.",
id, old_name, updated_name
);
Ok(())
}
pub async fn delete_stream(
&mut self,
session: &Session,
id: &Identifier,
) -> Result<u32, IggyError> {
self.ensure_authenticated(session)?;
let stream = self.get_stream(id)?;
let stream_id = stream.stream_id;
self.permissioner
.delete_stream(session.get_user_id(), stream_id)?;
let stream_name = stream.name.clone();
if stream.delete().await.is_err() {
return Err(IggyError::CannotDeleteStream(stream_id));
}
self.metrics.decrement_streams(1);
self.metrics.decrement_topics(stream.get_topics_count());
self.metrics
.decrement_partitions(stream.get_partitions_count());
self.metrics.decrement_messages(stream.get_messages_count());
self.metrics
.decrement_segments(stream.get_segments_count().await);
self.streams.remove(&stream_id);
self.streams_ids.remove(&stream_name);
let current_stream_id = CURRENT_STREAM_ID.load(Ordering::SeqCst);
if current_stream_id > stream_id {
CURRENT_STREAM_ID.store(stream_id, Ordering::SeqCst);
}
let client_manager = self.client_manager.read().await;
client_manager
.delete_consumer_groups_for_stream(stream_id)
.await;
Ok(stream_id)
}
pub async fn purge_stream(
&self,
session: &Session,
stream_id: &Identifier,
) -> Result<(), IggyError> {
let stream = self.get_stream(stream_id)?;
self.permissioner
.purge_stream(session.get_user_id(), stream.stream_id)?;
stream.purge().await
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::configs::server::PersonalAccessTokenConfig;
use crate::configs::system::SystemConfig;
use crate::streaming::storage::tests::get_test_system_storage;
use crate::streaming::users::user::User;
use iggy::users::defaults::{DEFAULT_ROOT_PASSWORD, DEFAULT_ROOT_USERNAME};
use std::{
net::{Ipv4Addr, SocketAddr},
sync::Arc,
};
#[tokio::test]
async fn should_get_stream_by_id_and_name() {
let stream_id = 1;
let stream_name = "test";
let config = Arc::new(SystemConfig::default());
let storage = get_test_system_storage();
let mut system =
System::create(config, storage, None, PersonalAccessTokenConfig::default());
let root = User::root(DEFAULT_ROOT_USERNAME, DEFAULT_ROOT_PASSWORD);
let session = Session::new(
1,
root.id,
SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 1234),
);
system.permissioner.init_permissions_for_user(root);
system
.create_stream(&session, Some(stream_id), stream_name)
.await
.unwrap();
let stream = system.get_stream(&Identifier::numeric(stream_id).unwrap());
assert!(stream.is_ok());
let stream = stream.unwrap();
assert_eq!(stream.stream_id, stream_id);
assert_eq!(stream.name, stream_name);
let stream = system.get_stream(&Identifier::named(stream_name).unwrap());
assert!(stream.is_ok());
let stream = stream.unwrap();
assert_eq!(stream.stream_id, stream_id);
assert_eq!(stream.name, stream_name);
}
}