@@ -53,7 +53,7 @@ use std::pin::Pin;
5353use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
5454use std:: sync:: { Arc , RwLock } ;
5555use std:: task:: { Context , Poll } ;
56- use sysinfo:: System ;
56+ use sysinfo:: { MemoryRefreshKind , RefreshKind , System } ;
5757use tokio:: runtime:: Runtime ;
5858use tokio_stream:: wrappers:: UnboundedReceiverStream ;
5959use tracing:: Instrument ;
@@ -82,9 +82,6 @@ type BoxedBatchStream = SendableRecordBatchStream;
8282type QueryResult = Result < ( Either < Vec < RecordBatch > , BoxedBatchStream > , Vec < String > ) , ExecuteError > ;
8383const DEFAULT_COUNTS_TOP_K : usize = 10 ;
8484
85- // pub static QUERY_SESSION: Lazy<SessionContext> =
86- // Lazy::new(|| Query::create_session_context(PARSEABLE.storage()));
87-
8885pub static QUERY_SESSION_STATE : Lazy < SessionState > =
8986 Lazy :: new ( || Query :: create_session_state ( PARSEABLE . storage ( ) ) ) ;
9087
@@ -147,10 +144,42 @@ impl InMemorySessionContext {
147144 }
148145}
149146
147+ async fn enough_available_memory ( ) -> Result < ( ) , ExecuteError > {
148+ let mut s = System :: new_with_specifics (
149+ RefreshKind :: nothing ( ) . with_memory ( MemoryRefreshKind :: everything ( ) ) ,
150+ ) ;
151+ s. refresh_all ( ) ;
152+ let threshold = ( PARSEABLE . options . query_mem_threshold / 100.0 ) as f64 ;
153+
154+ let f = async {
155+ loop {
156+ if let Some ( cgroup) = s. cgroup_limits ( ) {
157+ if ( cgroup. rss as f64 ) < threshold * ( cgroup. total_memory as f64 ) {
158+ return ;
159+ }
160+ } else {
161+ s. refresh_memory ( ) ;
162+ if ( s. used_memory ( ) as f64 ) < threshold * ( s. total_memory ( ) as f64 ) {
163+ return ;
164+ }
165+ }
166+ tokio:: time:: sleep ( tokio:: time:: Duration :: from_secs ( 1 ) ) . await ;
167+ }
168+ } ;
169+ if let Err ( e) = tokio:: time:: timeout ( tokio:: time:: Duration :: from_secs ( 10 ) , f) . await {
170+ Err ( ExecuteError :: ServerBusy ( e) )
171+ } else {
172+ Ok ( ( ) )
173+ }
174+ }
175+
150176/// This function executes a query on the dedicated runtime, ensuring that the query is not isolated to a single thread/CPU
151177/// at a time and has access to the entire thread pool, enabling better concurrent processing, and thus quicker results.
152178pub async fn execute ( query : Query , is_streaming : bool , tenant_id : & Option < String > ) -> QueryResult {
153179 let id = tenant_id. clone ( ) ;
180+
181+ // before executing query, check whether enough memory is available or not
182+ enough_available_memory ( ) . await ?;
154183 QUERY_RUNTIME
155184 . spawn ( async move {
156185 tokio:: time:: timeout (
@@ -220,7 +249,11 @@ impl Query {
220249 None => {
221250 let mut system = System :: new ( ) ;
222251 system. refresh_memory ( ) ;
223- let available_mem = system. available_memory ( ) ;
252+ let available_mem = if let Some ( cgroup) = system. cgroup_limits ( ) {
253+ cgroup. total_memory
254+ } else {
255+ system. total_memory ( )
256+ } ;
224257 ( available_mem as usize , 0.85 )
225258 }
226259 } ;
@@ -270,6 +303,8 @@ impl Query {
270303 . parquet
271304 . schema_force_view_types = true ;
272305
306+ config. options_mut ( ) . explain . show_statistics = true ;
307+
273308 SessionStateBuilder :: new ( )
274309 . with_default_features ( )
275310 . with_config ( config)
@@ -958,6 +993,7 @@ pub fn flatten_objects_for_count(objects: Vec<Value>) -> Vec<Value> {
958993
959994pub mod error {
960995 use crate :: { parseable:: StreamNotFound , storage:: ObjectStorageError } ;
996+ use actix_web:: http:: { StatusCode , header:: ContentType } ;
961997 use datafusion:: error:: DataFusionError ;
962998 use tokio:: time:: error:: Elapsed ;
963999
@@ -971,6 +1007,26 @@ pub mod error {
9711007 StreamNotFound ( #[ from] StreamNotFound ) ,
9721008 #[ error( "{0}: {1}s" ) ]
9731009 Timeout ( Elapsed , u64 ) ,
1010+ #[ error( "{0}: Not enough memory available to serve the request" ) ]
1011+ ServerBusy ( Elapsed ) ,
1012+ }
1013+
1014+ impl actix_web:: ResponseError for ExecuteError {
1015+ fn status_code ( & self ) -> StatusCode {
1016+ match self {
1017+ ExecuteError :: ObjectStorage ( _) => StatusCode :: INTERNAL_SERVER_ERROR ,
1018+ ExecuteError :: Datafusion ( _) => StatusCode :: INTERNAL_SERVER_ERROR ,
1019+ ExecuteError :: StreamNotFound ( _) => StatusCode :: NOT_FOUND ,
1020+ ExecuteError :: Timeout ( _, _) => StatusCode :: REQUEST_TIMEOUT ,
1021+ ExecuteError :: ServerBusy ( _) => StatusCode :: SERVICE_UNAVAILABLE ,
1022+ }
1023+ }
1024+
1025+ fn error_response ( & self ) -> actix_web:: HttpResponse < actix_web:: body:: BoxBody > {
1026+ actix_web:: HttpResponse :: build ( self . status_code ( ) )
1027+ . insert_header ( ContentType :: plaintext ( ) )
1028+ . body ( self . to_string ( ) )
1029+ }
9741030 }
9751031}
9761032
0 commit comments