1212// See the License for the specific language governing permissions and
1313// limitations under the License.
1414
15+ use std:: collections:: HashMap ;
16+
1517use databend_common_base:: base:: mask_connection_info;
1618use databend_common_base:: headers:: HEADER_QUERY_ID ;
1719use databend_common_base:: headers:: HEADER_QUERY_PAGE_ROWS ;
1820use databend_common_base:: headers:: HEADER_QUERY_STATE ;
1921use databend_common_base:: runtime:: drop_guard;
22+ use databend_common_base:: runtime:: execute_futures_in_parallel;
23+ use databend_common_base:: version:: DATABEND_SEMVER ;
24+ use databend_common_config:: GlobalConfig ;
2025use databend_common_exception:: ErrorCode ;
2126use databend_common_expression:: DataSchemaRef ;
2227use databend_common_metrics:: http:: metrics_incr_http_response_errors_count;
2328use fastrace:: func_path;
2429use fastrace:: prelude:: * ;
2530use highway:: HighwayHash ;
31+ use http:: HeaderMap ;
32+ use http:: HeaderValue ;
2633use http:: StatusCode ;
2734use log:: error;
2835use log:: info;
@@ -37,6 +44,7 @@ use poem::web::Json;
3744use poem:: web:: Path ;
3845use poem:: EndpointExt ;
3946use poem:: IntoResponse ;
47+ use poem:: Request ;
4048use poem:: Route ;
4149use serde:: Deserialize ;
4250use serde:: Serialize ;
@@ -45,8 +53,10 @@ use super::query::ExecuteStateKind;
4553use super :: query:: HttpQueryRequest ;
4654use super :: query:: HttpQueryResponseInternal ;
4755use super :: query:: RemoveReason ;
56+ use crate :: clusters:: ClusterDiscovery ;
4857use crate :: servers:: http:: error:: HttpErrorCode ;
4958use crate :: servers:: http:: error:: QueryError ;
59+ use crate :: servers:: http:: middleware:: forward_request_with_body;
5060use crate :: servers:: http:: middleware:: EndpointKind ;
5161use crate :: servers:: http:: middleware:: HTTPSessionMiddleware ;
5262use crate :: servers:: http:: middleware:: MetricsMiddleware ;
@@ -135,6 +145,7 @@ pub struct QueryResponse {
135145 pub schema : Vec < QueryResponseField > ,
136146 pub data : Vec < Vec < Option < String > > > ,
137147 pub affect : Option < QueryAffect > ,
148+ pub result_timeout_secs : Option < u64 > ,
138149
139150 pub stats : QueryStats ,
140151
@@ -208,6 +219,7 @@ impl QueryResponse {
208219 kill_uri : Some ( make_kill_uri ( & id) ) ,
209220 error : r. state . error . map ( QueryError :: from_error_code) ,
210221 has_result_set : r. state . has_result_set ,
222+ result_timeout_secs : Some ( r. result_timeout_secs ) ,
211223 } )
212224 . with_header ( HEADER_QUERY_ID , id. clone ( ) )
213225 . with_header ( HEADER_QUERY_STATE , state. state . to_string ( ) )
@@ -423,11 +435,116 @@ pub(crate) async fn query_handler(
423435 . await
424436}
425437
438+ #[ derive( Deserialize , Serialize , Debug ) ]
439+ struct HeartBeatRequest {
440+ node_to_queries : HashMap < String , Vec < String > > ,
441+ }
442+
443+ #[ derive( Deserialize , Serialize ) ]
444+ struct HeartBeatResponse {
445+ queries_to_remove : Vec < String > ,
446+ }
447+
448+ /// /v1/session/heartbeat are used for 2 purpose:
449+ /// 1. heartbeat to avoid session token/temp table expire
450+ /// 2. heartbeat to avoid result timeout of queries in this session
426451#[ poem:: handler]
427452#[ async_backtrace:: framed]
428- pub async fn heartbeat_handler ( ) -> poem:: error:: Result < impl IntoResponse > {
429- // work is already done in session manager
430- Ok ( ( ) )
453+ pub async fn heartbeat_handler (
454+ ctx : & HttpQueryContext ,
455+ req : & Request ,
456+ Json ( body) : Json < HeartBeatRequest > ,
457+ ) -> poem:: error:: Result < impl IntoResponse > {
458+ let local_id = GlobalConfig :: instance ( ) . query . node_id . clone ( ) ;
459+ let mut queries_to_remove = vec ! [ ] ;
460+ let mut nodes_to_forwards = vec ! [ ] ;
461+ for ( node_id, queries) in body. node_to_queries {
462+ if node_id == local_id {
463+ queries_to_remove. extend ( HttpQueryManager :: instance ( ) . on_heartbeat ( queries) ) ;
464+ } else if let Some ( node) = ClusterDiscovery :: instance ( )
465+ . find_node_by_id ( & node_id)
466+ . await
467+ . map_err ( HttpErrorCode :: server_error) ?
468+ {
469+ let mut node_to_queries = HashMap :: new ( ) ;
470+ node_to_queries. insert ( node_id. to_string ( ) , queries) ;
471+ let body = HeartBeatRequest { node_to_queries } ;
472+ let body = serde_json:: to_vec ( & body) . unwrap ( ) ;
473+ nodes_to_forwards. push ( ( node, body) ) ;
474+ } else {
475+ queries_to_remove. extend ( queries)
476+ }
477+ }
478+
479+ let num_task = nodes_to_forwards. len ( ) ;
480+ if num_task > 0 {
481+ let mut tasks = Vec :: with_capacity ( num_task) ;
482+ let uri = req. uri ( ) . to_string ( ) ;
483+ let method = req. method ( ) ;
484+ let mut headers = HeaderMap :: new ( ) ;
485+ headers. insert (
486+ http:: header:: CONTENT_TYPE ,
487+ HeaderValue :: from_static ( "application/json" ) ,
488+ ) ;
489+ let agent = format ! ( "databend-query/{}" , * DATABEND_SEMVER ) ;
490+ headers. insert (
491+ http:: header:: USER_AGENT ,
492+ HeaderValue :: from_str ( & agent) . unwrap ( ) ,
493+ ) ;
494+ headers. insert (
495+ http:: header:: AUTHORIZATION ,
496+ req. headers ( )
497+ . get ( http:: header:: AUTHORIZATION )
498+ . expect ( "heartbeat request should contain auth header" )
499+ . to_owned ( ) ,
500+ ) ;
501+ for ( node, body) in nodes_to_forwards {
502+ let uri = uri. clone ( ) ;
503+ let method = method. clone ( ) ;
504+ let headers = headers. clone ( ) ;
505+
506+ tasks. push ( async move {
507+ match forward_request_with_body ( node, & uri, body, method, headers) . await {
508+ Ok ( mut resp) => {
509+ if resp. status ( ) == StatusCode :: OK {
510+ Some (
511+ resp. take_body ( )
512+ . into_json :: < HeartBeatResponse > ( )
513+ . await
514+ . unwrap ( ) ,
515+ )
516+ } else {
517+ warn ! ( "heartbeat forward fail: {:?}" , resp) ;
518+ None
519+ }
520+ }
521+ Err ( e) => {
522+ warn ! ( "heartbeat forward error: {:?}" , e) ;
523+ None
524+ }
525+ }
526+ } ) ;
527+ }
528+ let settings = ctx. session . get_settings ( ) ;
529+ let num_threads = num_task. max (
530+ settings
531+ . get_max_threads ( )
532+ . map_err ( HttpErrorCode :: server_error) ? as usize ,
533+ ) ;
534+ let responses = execute_futures_in_parallel (
535+ tasks,
536+ num_threads,
537+ num_threads * 2 ,
538+ "forward_heartbeat" . to_owned ( ) ,
539+ )
540+ . await
541+ . map_err ( HttpErrorCode :: server_error) ?;
542+ for response in responses. into_iter ( ) . flatten ( ) {
543+ queries_to_remove. extend ( response. queries_to_remove ) ;
544+ }
545+ }
546+
547+ Ok ( Json ( HeartBeatResponse { queries_to_remove } ) . into_response ( ) )
431548}
432549
433550pub fn query_route ( ) -> Route {
0 commit comments