@@ -27,6 +27,7 @@ use crate::handlers::{
2727 CUSTOM_PARTITION_KEY , STATIC_SCHEMA_FLAG , TIME_PARTITION_KEY , TIME_PARTITION_LIMIT_KEY ,
2828 UPDATE_STREAM_KEY ,
2929} ;
30+ use crate :: hottier:: { HotTierManager , StreamHotTier } ;
3031use crate :: metadata:: STREAM_INFO ;
3132use crate :: metrics:: { EVENTS_INGESTED_DATE , EVENTS_INGESTED_SIZE_DATE , EVENTS_STORAGE_SIZE_DATE } ;
3233use crate :: option:: { Mode , CONFIG } ;
@@ -37,6 +38,7 @@ use crate::{
3738 catalog:: { self , remove_manifest_from_snapshot} ,
3839 event, stats,
3940} ;
41+
4042use crate :: { metadata, validator} ;
4143use actix_web:: http:: StatusCode ;
4244use actix_web:: { web, HttpRequest , Responder } ;
@@ -919,6 +921,122 @@ pub async fn get_stream_info(req: HttpRequest) -> Result<impl Responder, StreamE
919921 Ok ( ( web:: Json ( stream_info) , StatusCode :: OK ) )
920922}
921923
924+ pub async fn put_stream_hot_tier (
925+ req : HttpRequest ,
926+ body : web:: Json < serde_json:: Value > ,
927+ ) -> Result < impl Responder , StreamError > {
928+ if CONFIG . parseable . mode != Mode :: Query {
929+ return Err ( StreamError :: Custom {
930+ msg : "Hot tier can only be enabled in query mode" . to_string ( ) ,
931+ status : StatusCode :: BAD_REQUEST ,
932+ } ) ;
933+ }
934+ let stream_name: String = req. match_info ( ) . get ( "logstream" ) . unwrap ( ) . parse ( ) . unwrap ( ) ;
935+ if !metadata:: STREAM_INFO . stream_exists ( & stream_name) {
936+ return Err ( StreamError :: StreamNotFound ( stream_name) ) ;
937+ }
938+ if CONFIG . parseable . hot_tier_storage_path . is_none ( ) {
939+ return Err ( StreamError :: HotTierNotEnabled ( stream_name) ) ;
940+ }
941+
942+ if STREAM_INFO
943+ . get_time_partition ( & stream_name)
944+ . unwrap ( )
945+ . is_some ( )
946+ {
947+ return Err ( StreamError :: Custom {
948+ msg : "Hot tier can not be enabled for stream with time partition" . to_string ( ) ,
949+ status : StatusCode :: BAD_REQUEST ,
950+ } ) ;
951+ }
952+
953+ let body = body. into_inner ( ) ;
954+ let mut hottier: StreamHotTier = match serde_json:: from_value ( body) {
955+ Ok ( hottier) => hottier,
956+ Err ( err) => return Err ( StreamError :: InvalidHotTierConfig ( err) ) ,
957+ } ;
958+
959+ validator:: hot_tier ( & hottier. size . to_string ( ) ) ?;
960+
961+ STREAM_INFO . set_hot_tier ( & stream_name, true ) ?;
962+ if let Some ( hot_tier_manager) = HotTierManager :: global ( ) {
963+ hot_tier_manager
964+ . validate_hot_tier_size ( & stream_name, & hottier. size )
965+ . await ?;
966+ hottier. used_size = Some ( "0GiB" . to_string ( ) ) ;
967+ hottier. available_size = Some ( hottier. size . clone ( ) ) ;
968+ hot_tier_manager
969+ . put_hot_tier ( & stream_name, & mut hottier)
970+ . await ?;
971+ let storage = CONFIG . storage ( ) . get_object_store ( ) ;
972+ let mut stream_metadata = storage. get_object_store_format ( & stream_name) . await ?;
973+ stream_metadata. hot_tier_enabled = Some ( true ) ;
974+ storage
975+ . put_stream_manifest ( & stream_name, & stream_metadata)
976+ . await ?;
977+ }
978+
979+ Ok ( (
980+ format ! ( "hot tier set for stream {stream_name}" ) ,
981+ StatusCode :: OK ,
982+ ) )
983+ }
984+
985+ pub async fn get_stream_hot_tier ( req : HttpRequest ) -> Result < impl Responder , StreamError > {
986+ if CONFIG . parseable . mode != Mode :: Query {
987+ return Err ( StreamError :: Custom {
988+ msg : "Hot tier can only be enabled in query mode" . to_string ( ) ,
989+ status : StatusCode :: BAD_REQUEST ,
990+ } ) ;
991+ }
992+
993+ let stream_name: String = req. match_info ( ) . get ( "logstream" ) . unwrap ( ) . parse ( ) . unwrap ( ) ;
994+
995+ if !metadata:: STREAM_INFO . stream_exists ( & stream_name) {
996+ return Err ( StreamError :: StreamNotFound ( stream_name) ) ;
997+ }
998+
999+ if CONFIG . parseable . hot_tier_storage_path . is_none ( ) {
1000+ return Err ( StreamError :: HotTierNotEnabled ( stream_name) ) ;
1001+ }
1002+
1003+ if let Some ( hot_tier_manager) = HotTierManager :: global ( ) {
1004+ let hot_tier = hot_tier_manager. get_hot_tier ( & stream_name) . await ?;
1005+ Ok ( ( web:: Json ( hot_tier) , StatusCode :: OK ) )
1006+ } else {
1007+ Err ( StreamError :: Custom {
1008+ msg : format ! ( "hot tier not initialised for stream {}" , stream_name) ,
1009+ status : ( StatusCode :: BAD_REQUEST ) ,
1010+ } )
1011+ }
1012+ }
1013+
1014+ pub async fn delete_stream_hot_tier ( req : HttpRequest ) -> Result < impl Responder , StreamError > {
1015+ if CONFIG . parseable . mode != Mode :: Query {
1016+ return Err ( StreamError :: Custom {
1017+ msg : "Hot tier can only be enabled in query mode" . to_string ( ) ,
1018+ status : StatusCode :: BAD_REQUEST ,
1019+ } ) ;
1020+ }
1021+
1022+ let stream_name: String = req. match_info ( ) . get ( "logstream" ) . unwrap ( ) . parse ( ) . unwrap ( ) ;
1023+
1024+ if !metadata:: STREAM_INFO . stream_exists ( & stream_name) {
1025+ return Err ( StreamError :: StreamNotFound ( stream_name) ) ;
1026+ }
1027+
1028+ if CONFIG . parseable . hot_tier_storage_path . is_none ( ) {
1029+ return Err ( StreamError :: HotTierNotEnabled ( stream_name) ) ;
1030+ }
1031+
1032+ if let Some ( hot_tier_manager) = HotTierManager :: global ( ) {
1033+ hot_tier_manager. delete_hot_tier ( & stream_name) . await ?;
1034+ }
1035+ Ok ( (
1036+ format ! ( "hot tier deleted for stream {stream_name}" ) ,
1037+ StatusCode :: OK ,
1038+ ) )
1039+ }
9221040#[ allow( unused) ]
9231041fn classify_json_error ( kind : serde_json:: error:: Category ) -> StatusCode {
9241042 match kind {
@@ -935,9 +1053,12 @@ pub mod error {
9351053 use http:: StatusCode ;
9361054
9371055 use crate :: {
1056+ hottier:: HotTierError ,
9381057 metadata:: error:: stream_info:: MetadataError ,
9391058 storage:: ObjectStorageError ,
940- validator:: error:: { AlertValidationError , StreamNameValidationError } ,
1059+ validator:: error:: {
1060+ AlertValidationError , HotTierValidationError , StreamNameValidationError ,
1061+ } ,
9411062 } ;
9421063
9431064 #[ allow( unused) ]
@@ -997,6 +1118,16 @@ pub mod error {
9971118 Network ( #[ from] reqwest:: Error ) ,
9981119 #[ error( "Could not deserialize into JSON object, {0}" ) ]
9991120 SerdeError ( #[ from] serde_json:: Error ) ,
1121+ #[ error(
1122+ "Hot tier is not enabled at the server config, cannot enable hot tier for stream {0}"
1123+ ) ]
1124+ HotTierNotEnabled ( String ) ,
1125+ #[ error( "failed to enable hottier due to err: {0}" ) ]
1126+ InvalidHotTierConfig ( serde_json:: Error ) ,
1127+ #[ error( "Hot tier validation failed due to {0}" ) ]
1128+ HotTierValidation ( #[ from] HotTierValidationError ) ,
1129+ #[ error( "{0}" ) ]
1130+ HotTierError ( #[ from] HotTierError ) ,
10001131 }
10011132
10021133 impl actix_web:: ResponseError for StreamError {
@@ -1030,6 +1161,10 @@ pub mod error {
10301161 StreamError :: Network ( err) => {
10311162 err. status ( ) . unwrap_or ( StatusCode :: INTERNAL_SERVER_ERROR )
10321163 }
1164+ StreamError :: HotTierNotEnabled ( _) => StatusCode :: BAD_REQUEST ,
1165+ StreamError :: InvalidHotTierConfig ( _) => StatusCode :: BAD_REQUEST ,
1166+ StreamError :: HotTierValidation ( _) => StatusCode :: BAD_REQUEST ,
1167+ StreamError :: HotTierError ( _) => StatusCode :: INTERNAL_SERVER_ERROR ,
10331168 }
10341169 }
10351170
0 commit comments