1use crate::common::context::PipelineContext;
2use crate::common::interface::middleware_manager::MiddlewareManager;
3use crate::common::model::data::DataEvent;
4use crate::common::model::message::{TaskErrorEvent, TaskEvent};
5use crate::common::model::{ModuleConfig, Response};
6use crate::common::status_tracker::ErrorDecision;
7use crate::engine::events::{
8 DataMiddlewareEvent, DataStoreEvent, EventBus, EventEnvelope, EventPhase, EventType,
9 ModuleGenerateEvent, ParserEvent,
10};
11use crate::engine::processors::event_processor::{EventAwareTypedChain, EventProcessorTrait};
12use crate::engine::task::TaskManager;
13use crate::errors::{DataMiddlewareError, Error, Result};
14use async_trait::async_trait;
15
16use crate::cacheable::{CacheAble, CacheService};
17use crate::common::model::login_info::LoginInfo;
18use crate::common::processors::processor::{
19 ProcessorContext, ProcessorResult, ProcessorTrait, RetryPolicy,
20};
21use crate::common::processors::processor_chain::ErrorStrategy;
22use crate::engine::chain::backpressure::{BackpressureSendState, send_with_backpressure};
23use crate::engine::task::module::Module;
24use crate::queue::{QueueManager, QueuedItem};
25use dashmap::DashMap;
26use futures::StreamExt;
27use log::{debug, error, info, warn};
28use metrics::counter;
29use serde_json::json;
30use std::sync::Arc;
31use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
32
33pub struct ResponseModuleProcessor {
35 #[allow(dead_code)]
36 task_manager: Arc<TaskManager>,
37 cache_service: Arc<CacheService>,
38 state: Arc<PipelineContext>,
39 config_cache: Arc<DashMap<String, (Arc<ModuleConfig>, Instant)>>,
40}
41#[async_trait]
42impl ProcessorTrait<Response, (Response, Arc<Module>, Arc<ModuleConfig>, Option<LoginInfo>)>
43 for ResponseModuleProcessor
44{
45 fn name(&self) -> &'static str {
46 "ResponseModuleProcessor"
47 }
48
49 async fn process(
50 &self,
51 input: Response,
52 context: ProcessorContext,
53 ) -> ProcessorResult<(Response, Arc<Module>, Arc<ModuleConfig>, Option<LoginInfo>)> {
54 match self
64 .state
65 .status_tracker
66 .should_task_continue(&input.task_runtime_id())
67 .await
68 {
69 Ok(ErrorDecision::Continue) => {
70 }
72 Ok(ErrorDecision::Terminate(reason)) => {
73 error!(
74 "[ResponseModuleProcessor] task terminated before parsing: task_id={} reason={}",
75 input.task_runtime_id(),
76 reason
77 );
78 self.state
80 .status_tracker
81 .release_module_locker(&input.module_runtime_id())
82 .await;
83 return ProcessorResult::FatalFailure(
84 crate::errors::ModuleError::TaskMaxError(reason.into()).into(),
85 );
86 }
87 Err(e) => {
88 warn!(
89 "[ResponseModuleProcessor] task error check failed, continue anyway: task_id={} error={}",
90 input.task_runtime_id(),
91 e
92 );
93 }
94 _ => {}
95 }
96
97 match self
99 .state
100 .status_tracker
101 .should_module_continue(&input.module_runtime_id())
102 .await
103 {
104 Ok(ErrorDecision::Continue) => {
105 }
107 Ok(ErrorDecision::Terminate(reason)) => {
108 error!(
109 "[ResponseModuleProcessor] module terminated before parsing: module_id={} reason={}",
110 input.module_runtime_id(),
111 reason
112 );
113 self.state
115 .status_tracker
116 .release_module_locker(&input.module_runtime_id())
117 .await;
118 return ProcessorResult::FatalFailure(
119 crate::errors::ModuleError::ModuleMaxError(reason.into()).into(),
120 );
121 }
122 Err(e) => {
123 warn!(
124 "[ResponseModuleProcessor] module error check failed, continue anyway: module_id={} error={}",
125 input.module_runtime_id(),
126 e
127 );
128 }
129 _ => {}
130 }
131
132 let task: Result<(Arc<Module>, Option<LoginInfo>)> =
133 self.task_manager.load_module_with_response(&input).await;
134 match task {
135 Ok((module, login_info)) => {
136 let module_id = module.id();
138 let now = Instant::now();
139 let mut should_prune_cache = false;
140 let cached_config = if let Some(entry) = self.config_cache.get(&module_id) {
141 let (cfg, expires_at) = entry.value();
142 if now < *expires_at {
143 Some(cfg.clone())
144 } else {
145 should_prune_cache = true;
146 None
147 }
148 } else {
149 None
150 };
151
152 if should_prune_cache {
153 self.config_cache.remove(&module_id);
154 }
155
156 let config = if let Some(c) = cached_config {
157 c
158 } else {
159 match ModuleConfig::sync(&module_id, &self.cache_service).await {
160 Ok(Some(config)) => {
161 let config = Arc::new(config);
162 self.config_cache.insert(
163 module_id,
164 (config.clone(), Instant::now() + Duration::from_secs(10)),
165 );
166 config
167 }
168 _ => module.config.clone(),
169 }
170 };
171
172 ProcessorResult::Success((input, module, config, login_info))
178 }
179 Err(e) => {
180 warn!(
181 "[ResponseModuleProcessor] load_with_response failed, will retry: account={} platform={} request_id={} err={e}",
182 input.account, input.platform, input.id
183 );
184 ProcessorResult::RetryableFailure(
185 context
186 .retry_policy
187 .unwrap_or(RetryPolicy::default().with_reason(e.to_string())),
188 )
189 }
190 }
191 }
192 async fn pre_process(&self, input: &Response, _context: &ProcessorContext) -> Result<()> {
193 if self.state.config.read().await.download_config.enable_locker {
194 debug!(
195 "[ResponseModuleProcessor] lock module before parsing: module_id={}",
196 input.module_runtime_id()
197 );
198 self.state
199 .status_tracker
200 .lock_module(&input.module_runtime_id())
201 .await;
202 }
203 Ok(())
204 }
205}
206#[async_trait]
207impl EventProcessorTrait<Response, (Response, Arc<Module>, Arc<ModuleConfig>, Option<LoginInfo>)>
208 for ResponseModuleProcessor
209{
210 fn pre_status(&self, input: &Response) -> Option<EventEnvelope> {
211 Some(EventEnvelope::engine(
212 EventType::ModuleGenerate,
213 EventPhase::Started,
214 ModuleGenerateEvent::from(input),
215 ))
216 }
217
218 fn finish_status(
219 &self,
220 input: &Response,
221 _output: &(Response, Arc<Module>, Arc<ModuleConfig>, Option<LoginInfo>),
222 ) -> Option<EventEnvelope> {
223 Some(EventEnvelope::engine(
224 EventType::ModuleGenerate,
225 EventPhase::Completed,
226 ModuleGenerateEvent::from(input),
227 ))
228 }
229
230 fn working_status(&self, input: &Response) -> Option<EventEnvelope> {
231 Some(EventEnvelope::engine(
232 EventType::ModuleGenerate,
233 EventPhase::Started,
234 ModuleGenerateEvent::from(input),
235 ))
236 }
237
238 fn error_status(&self, input: &Response, err: &Error) -> Option<EventEnvelope> {
239 Some(EventEnvelope::engine_error(
240 EventType::ModuleGenerate,
241 EventPhase::Failed,
242 ModuleGenerateEvent::from(input),
243 err,
244 ))
245 }
246
247 fn retry_status(&self, input: &Response, retry_policy: &RetryPolicy) -> Option<EventEnvelope> {
248 Some(EventEnvelope::engine(
249 EventType::ModuleGenerate,
250 EventPhase::Retry,
251 json!({
252 "data": ModuleGenerateEvent::from(input),
253 "retry_count": retry_policy.current_retry,
254 "reason": retry_policy.reason.clone().unwrap_or_default(),
255 }),
256 ))
257 }
258}
259
260pub struct ResponseParserProcessor {
261 #[allow(dead_code)]
262 task_manager: Arc<TaskManager>,
263 queue_manager: Arc<QueueManager>,
264 state: Arc<PipelineContext>,
265 cache_service: Arc<CacheService>,
266 event_bus: Option<Arc<EventBus>>,
267}
268
269impl ResponseParserProcessor {
270 async fn emit_semantic_event(&self, event_type: EventType, payload: serde_json::Value) {
271 if let Some(event_bus) = &self.event_bus {
272 let _ = event_bus
273 .publish(EventEnvelope::engine(
274 event_type,
275 EventPhase::Completed,
276 payload,
277 ))
278 .await;
279 }
280 }
281
282 async fn send_with_backpressure<T>(
283 &self,
284 tx: &tokio::sync::mpsc::Sender<QueuedItem<T>>,
285 item: QueuedItem<T>,
286 queue_kind: &'static str,
287 log_context: &str,
288 ) -> bool {
289 match send_with_backpressure(tx, item).await {
290 Ok(BackpressureSendState::Direct) => true,
291 Ok(BackpressureSendState::RecoveredFromFull) => {
292 counter!("mocra_parser_chain_backpressure_total", "queue" => queue_kind, "reason" => "queue_full").increment(1);
293 warn!(
294 "[ResponseParserProcessor] queue full, fallback to awaited send: queue={} context={} remaining_capacity={}",
295 queue_kind,
296 log_context,
297 tx.capacity()
298 );
299 true
300 }
301 Err(err) => {
302 if err.after_full {
303 counter!("mocra_parser_chain_backpressure_total", "queue" => queue_kind, "reason" => "queue_full").increment(1);
304 warn!(
305 "[ResponseParserProcessor] queue full before close: queue={} context={} remaining_capacity={}",
306 queue_kind,
307 log_context,
308 tx.capacity()
309 );
310 }
311 counter!("mocra_parser_chain_backpressure_total", "queue" => queue_kind, "reason" => "queue_closed").increment(1);
312 error!(
313 "[ResponseParserProcessor] queue closed before send: queue={} context={}",
314 queue_kind, log_context
315 );
316 false
317 }
318 }
319 }
320}
321
322#[async_trait]
323impl ProcessorTrait<(Response, Arc<Module>, Arc<ModuleConfig>, Option<LoginInfo>), Vec<DataEvent>>
324 for ResponseParserProcessor
325{
326 fn name(&self) -> &'static str {
327 "ResponseParserProcessor"
328 }
329
330 async fn process(
331 &self,
332 input: (Response, Arc<Module>, Arc<ModuleConfig>, Option<LoginInfo>),
333 context: ProcessorContext,
334 ) -> ProcessorResult<Vec<DataEvent>> {
335 info!(
336 "[ResponseParserProcessor] start parse: account={} platform={} module={} request_id={} module_id={}",
337 input.0.account,
338 input.0.platform,
339 input.0.module,
340 input.0.id,
341 input.0.module_id()
342 );
343 let module = input.1.clone();
344 let config = input.2.clone();
345 let login_info = input.3.clone();
346
347 match serde_json::to_value(config.as_ref()) {
350 Ok(cfg_val) => {
351 context
352 .metadata
353 .write()
354 .await
355 .insert("config".to_string(), cfg_val);
356 }
357 Err(e) => {
358 warn!(
359 "[ResponseParserProcessor] failed to serialize module config into context: request_id={} module_id={} error={}",
360 input.0.id,
361 input.0.module_id(),
362 e
363 );
364 }
365 }
366
367 let task_id = input.0.task_runtime_id();
369 let module_id = input.0.module_runtime_id();
370 let request_id = input.0.id.to_string();
371 let account = input.0.account.clone();
372 let platform = input.0.platform.clone();
373 let module_name = input.0.module.clone();
374
375 let data = module.parser(input.0.clone(), Some(config)).await;
376 let mut data = match data {
377 Ok(d) => {
378 let has_next_task = !d.parser_task.is_empty();
379 let has_error = d.error_task.is_some();
380 info!(
381 "[ResponseParserProcessor] parser returned: request_id={} data_len={} has_next_task={} has_error={}",
382 request_id,
383 d.data.len(),
384 has_next_task,
385 has_error
386 );
387
388 self.state
390 .status_tracker
391 .record_parse_success(&request_id)
392 .await
393 .ok();
394
395 d
396 }
397 Err(e) => {
398 warn!(
399 "[ResponseParserProcessor] parser error: account={} platform={} module={} request_id={} error={e}",
400 account, platform, module_name, request_id
401 );
402
403 match self
405 .state
406 .status_tracker
407 .record_parse_error(&task_id, &module_id, &request_id, &e)
408 .await
409 {
410 Ok(ErrorDecision::Continue) | Ok(ErrorDecision::RetryAfter(_)) => {
411 debug!(
412 "[ResponseParserProcessor] will retry parsing: request_id={}",
413 request_id
414 );
415 return ProcessorResult::RetryableFailure(
416 context
417 .retry_policy
418 .unwrap_or(RetryPolicy::default().with_reason(e.to_string())),
419 );
420 }
421 Ok(ErrorDecision::Skip) => {
422 warn!(
423 "[ResponseParserProcessor] skip parse after max retries: request_id={}",
424 request_id
425 );
426 return ProcessorResult::Success(vec![]);
428 }
429 Ok(ErrorDecision::Terminate(reason)) => {
430 error!("[ResponseParserProcessor] terminate: {}", reason);
431 return ProcessorResult::FatalFailure(e);
432 }
433 Err(err) => {
434 error!("[ResponseParserProcessor] error tracker failed: {}", err);
435 return ProcessorResult::FatalFailure(e);
436 }
437 }
438 }
439 };
440
441 let parser_task_queue = self.queue_manager.get_parser_task_push_channel();
442 for task in data.parser_task.drain(..) {
443 let task_account = task.account_task.account.clone();
444 let task_platform = task.account_task.platform.clone();
445 let task_run_id = task.run_id;
446 let task_module_id = task.context.module_id.clone();
447 let task_step_idx = task.context.step_idx;
448 let task_prefix_request = task.prefix_request;
449 let target_module_name = task.account_task.module.as_ref().and_then(|v| v.first());
451 let is_same_module =
452 target_module_name.is_none_or(|name| name == module.module.name().as_str());
453
454 let is_same_context = task.account_task.account == module.account.name
456 && task.account_task.platform == module.platform.name;
457
458 if is_same_module && is_same_context {
459 debug!(
460 "[ResponseParserProcessor] Optimizing: Generating requests locally for same module"
461 );
462
463 let mut module_clone = (*module).clone();
465 module_clone.pending_ctx = Some(task.context.clone());
466 module_clone.prefix_request = task.prefix_request;
467 module_clone.run_id = task.run_id;
468
469 let task_meta = task.metadata.clone();
470
471 match module_clone.generate(task_meta, login_info.clone()).await {
472 Ok(mut stream) => {
473 let request_queue = self.queue_manager.get_request_push_channel();
474 let mut generated_count = 0;
475 while let Some(req) = stream.next().await {
476 let req_id = req.id.to_string();
477 let req_module_id = req.module_id();
478 let context = format!(
479 "generated_request request_id={} module_id={}",
480 req_id, req_module_id
481 );
482 if self
483 .send_with_backpressure(
484 &request_queue,
485 QueuedItem::new(req),
486 "request",
487 &context,
488 )
489 .await
490 {
491 generated_count += 1;
492 }
493 }
494 info!(
495 "[ResponseParserProcessor] Locally generated {} requests",
496 generated_count
497 );
498 self.emit_semantic_event(
499 EventType::ParserTaskProduced,
500 json!({
501 "account": task_account,
502 "platform": task_platform,
503 "run_id": task_run_id,
504 "module_id": task_module_id,
505 "step_idx": task_step_idx,
506 "prefix_request": task_prefix_request,
507 "path": "local_generate"
508 }),
509 )
510 .await;
511 self.emit_semantic_event(
512 EventType::ModuleStepAdvanced,
513 json!({
514 "account": task_account,
515 "platform": task_platform,
516 "run_id": task_run_id,
517 "module_id": task_module_id,
518 "step_idx": task_step_idx,
519 "prefix_request": task_prefix_request,
520 "generated_count": generated_count,
521 "mode": "local_generate"
522 }),
523 )
524 .await;
525 }
527 Err(e) => {
528 error!(
529 "[ResponseParserProcessor] Failed to generate requests locally: {}, falling back to queue",
530 e
531 );
532 let context = format!(
534 "parser_task_fallback account={} platform={} run_id={} module_id={}",
535 task.account_task.account,
536 task.account_task.platform,
537 task.run_id,
538 task.context.module_id.as_deref().unwrap_or("unknown")
539 );
540 if !self
541 .send_with_backpressure(
542 &parser_task_queue,
543 QueuedItem::new(task),
544 "parser_task",
545 &context,
546 )
547 .await
548 {
549 error!(
550 "[ResponseParserProcessor] failed to send parser task fallback: {}",
551 context
552 );
553 } else {
554 self.emit_semantic_event(
555 EventType::ParserTaskProduced,
556 json!({
557 "account": task_account,
558 "platform": task_platform,
559 "run_id": task_run_id,
560 "module_id": task_module_id,
561 "step_idx": task_step_idx,
562 "prefix_request": task_prefix_request,
563 "path": "fallback_queue"
564 }),
565 )
566 .await;
567 self.emit_semantic_event(
568 EventType::ModuleStepFallback,
569 json!({
570 "account": task_account,
571 "platform": task_platform,
572 "run_id": task_run_id,
573 "module_id": task_module_id,
574 "step_idx": task_step_idx,
575 "prefix_request": task_prefix_request,
576 "reason": "local_generate_failed",
577 "error": e.to_string(),
578 "path": "parser_task_queue"
579 }),
580 )
581 .await;
582 }
583 }
584 }
585 } else {
586 let context = format!(
588 "parser_task account={} platform={} run_id={} module_id={}",
589 task.account_task.account,
590 task.account_task.platform,
591 task.run_id,
592 task.context.module_id.as_deref().unwrap_or("unknown")
593 );
594 if !self
595 .send_with_backpressure(
596 &parser_task_queue,
597 QueuedItem::new(task),
598 "parser_task",
599 &context,
600 )
601 .await
602 {
603 error!(
604 "[ResponseParserProcessor] failed to send parser task: {}",
605 context
606 );
607 } else {
608 self.emit_semantic_event(
609 EventType::ParserTaskProduced,
610 json!({
611 "account": task_account,
612 "platform": task_platform,
613 "run_id": task_run_id,
614 "module_id": task_module_id,
615 "step_idx": task_step_idx,
616 "prefix_request": task_prefix_request,
617 "path": "parser_task_queue"
618 }),
619 )
620 .await;
621 }
622 }
623 }
624
625 if let Some(mut msg) = data.error_task {
626 warn!(
627 "[ResponseParserProcessor] recorded response error for request_id={}, message={}",
628 input.0.id, msg.error_msg
629 );
630 msg.prefix_request = input.0.prefix_request;
631 msg.run_id = input.0.run_id;
632 let queue = self.queue_manager.get_error_push_channel();
633 let context = format!(
634 "error_task request_id={} module_id={}",
635 input.0.id,
636 input.0.module_id()
637 );
638 if !self
639 .send_with_backpressure(&queue, QueuedItem::new(msg), "error", &context)
640 .await
641 {
642 error!(
643 "[ResponseParserProcessor] failed to send parser error: {}",
644 context
645 );
646 } else {
647 self.emit_semantic_event(
648 EventType::ErrorTaskProduced,
649 json!({
650 "request_id": input.0.id,
651 "account": input.0.account,
652 "platform": input.0.platform,
653 "module_id": input.0.module_id(),
654 "step_idx": input.0.context.step_idx,
655 "prefix_request": input.0.prefix_request
656 }),
657 )
658 .await;
659 }
660
661 }
664
665 data.data.iter_mut().for_each(|x| {
666 x.account = input.0.account.clone();
667 x.platform = input.0.platform.clone();
668 if x.data_middleware.is_empty() {
670 x.data_middleware = input.0.data_middleware.clone();
671 }
672 x.request_id = input.0.id;
673 x.meta = input.0.metadata.clone();
674 });
675 ProcessorResult::Success(data.data)
676 }
677 async fn post_process(
678 &self,
679 input: &(Response, Arc<Module>, Arc<ModuleConfig>, Option<LoginInfo>),
680 _output: &Vec<DataEvent>,
681 _context: &ProcessorContext,
682 ) -> Result<()> {
683 let config = self.state.config.read().await;
686
687 if config.download_config.enable_session {
689 if let Some(request_hash) = &input.0.request_hash {
690 input.0.send(request_hash, &self.cache_service).await.ok();
691 }
692 }
693
694 if config.download_config.enable_locker {
695 self.state
696 .status_tracker
697 .release_module_locker(&input.0.module_runtime_id())
698 .await;
699 debug!(
700 "[ResponseParserProcessor] released module lock after parsing: module_id={}",
701 input.0.module_runtime_id()
702 );
703 }
704 Ok(())
705 }
706 async fn handle_error(
707 &self,
708 input: &(Response, Arc<Module>, Arc<ModuleConfig>, Option<LoginInfo>),
709 error: Error,
710 _context: &ProcessorContext,
711 ) -> ProcessorResult<Vec<DataEvent>> {
712 error!(
713 "[ResponseParserProcessor] fatal parser error: request_id={} module_id={} error={}",
714 input.0.id,
715 input.0.module_id(),
716 error
717 );
718
719 let timestamp = match SystemTime::now().duration_since(UNIX_EPOCH) {
723 Ok(duration) => duration.as_secs(),
724 Err(e) => {
725 warn!(
726 "[ResponseParserProcessor] system time before UNIX_EPOCH, fallback to 0: {}",
727 e
728 );
729 0
730 }
731 };
732 let error_metadata = input
733 .0
734 .metadata
735 .task
736 .as_object()
737 .cloned()
738 .unwrap_or_default();
739 let error_task = TaskErrorEvent {
740 id: input.0.id,
741 account_task: TaskEvent {
742 account: input.0.account.clone(),
743 platform: input.0.platform.clone(),
744 module: Some(vec![input.0.module.clone()]),
745 run_id: input.0.run_id,
746 priority: input.0.priority,
747 },
748 error_msg: error.to_string(),
749 timestamp,
750 metadata: error_metadata,
751 context: input.0.context.clone(),
752 run_id: input.0.run_id,
753 prefix_request: input.0.prefix_request,
754 };
755 let queue = self.queue_manager.get_error_push_channel();
756 let context = format!(
757 "handle_error request_id={} module_id={}",
758 input.0.id,
759 input.0.module_id()
760 );
761 if !self
762 .send_with_backpressure(&queue, QueuedItem::new(error_task), "error", &context)
763 .await
764 {
765 error!(
766 "[ResponseParserProcessor] failed to enqueue ErrorTaskModel: {}",
767 context
768 );
769 }
770
771 if self.state.config.read().await.download_config.enable_locker {
773 self.state
774 .status_tracker
775 .release_module_locker(&input.0.module_runtime_id())
776 .await;
777 }
778
779 ProcessorResult::FatalFailure(error)
780 }
781}
782impl
783 EventProcessorTrait<
784 (Response, Arc<Module>, Arc<ModuleConfig>, Option<LoginInfo>),
785 Vec<DataEvent>,
786 > for ResponseParserProcessor
787{
788 fn pre_status(
789 &self,
790 input: &(Response, Arc<Module>, Arc<ModuleConfig>, Option<LoginInfo>),
791 ) -> Option<EventEnvelope> {
792 Some(EventEnvelope::engine(
793 EventType::Parser,
794 EventPhase::Started,
795 ParserEvent::from(&input.0),
796 ))
797 }
798
799 fn finish_status(
800 &self,
801 input: &(Response, Arc<Module>, Arc<ModuleConfig>, Option<LoginInfo>),
802 _output: &Vec<DataEvent>,
803 ) -> Option<EventEnvelope> {
804 Some(EventEnvelope::engine(
805 EventType::Parser,
806 EventPhase::Completed,
807 ParserEvent::from(&input.0),
808 ))
809 }
810
811 fn working_status(
812 &self,
813 input: &(Response, Arc<Module>, Arc<ModuleConfig>, Option<LoginInfo>),
814 ) -> Option<EventEnvelope> {
815 Some(EventEnvelope::engine(
816 EventType::Parser,
817 EventPhase::Started,
818 ParserEvent::from(&input.0),
819 ))
820 }
821
822 fn error_status(
823 &self,
824 input: &(Response, Arc<Module>, Arc<ModuleConfig>, Option<LoginInfo>),
825 err: &Error,
826 ) -> Option<EventEnvelope> {
827 Some(EventEnvelope::engine_error(
828 EventType::Parser,
829 EventPhase::Failed,
830 ParserEvent::from(&input.0),
831 err,
832 ))
833 }
834
835 fn retry_status(
836 &self,
837 input: &(Response, Arc<Module>, Arc<ModuleConfig>, Option<LoginInfo>),
838 retry_policy: &RetryPolicy,
839 ) -> Option<EventEnvelope> {
840 Some(EventEnvelope::engine(
841 EventType::Parser,
842 EventPhase::Retry,
843 json!({
844 "data": ParserEvent::from(&input.0),
845 "retry_count": retry_policy.current_retry,
846 "reason": retry_policy.reason.clone().unwrap_or_default(),
847 }),
848 ))
849 }
850}
851
852pub struct DataMiddlewareProcessor {
853 middleware_manager: Arc<MiddlewareManager>,
854}
855
856#[async_trait]
857impl ProcessorTrait<DataEvent, DataEvent> for DataMiddlewareProcessor {
858 fn name(&self) -> &'static str {
859 "DataMiddlewareProcessor"
860 }
861
862 async fn process(
863 &self,
864 input: DataEvent,
865 context: ProcessorContext,
866 ) -> ProcessorResult<DataEvent> {
867 let start = std::time::Instant::now();
874 let config = context
875 .metadata
876 .read()
877 .await
878 .get("config")
879 .map(|x| {
880 serde_json::from_value::<ModuleConfig>(x.clone()).unwrap_or_else(|e| {
881 warn!(
882 "[DataMiddlewareProcessor] config conversion failed, using default: request_id={} module={} error={}",
883 input.request_id,
884 input.module,
885 e
886 );
887 ModuleConfig::default()
888 })
889 });
890 let modified_data = self.middleware_manager.handle_data(input, &config).await;
891 let elapsed_ms = start.elapsed().as_millis();
892 if elapsed_ms > 10 {
893 info!(
894 "[DataMiddlewareProcessor] SLOW middleware execution: {} ms",
895 elapsed_ms
896 );
897 }
898 match modified_data {
899 Some(data) => ProcessorResult::Success(data),
900 None => ProcessorResult::FatalFailure(
901 DataMiddlewareError::EmptyData("data dropped by data middleware".into()).into(),
902 ),
903 }
904 }
905}
906impl EventProcessorTrait<DataEvent, DataEvent> for DataMiddlewareProcessor {
907 fn pre_status(&self, input: &DataEvent) -> Option<EventEnvelope> {
908 Some(EventEnvelope::engine(
909 EventType::MiddlewareBefore,
910 EventPhase::Started,
911 DataMiddlewareEvent::from(input),
912 ))
913 }
914
915 fn finish_status(&self, input: &DataEvent, output: &DataEvent) -> Option<EventEnvelope> {
916 let mut event: DataMiddlewareEvent = input.into();
917 event.after_size = output.size().into();
918 Some(EventEnvelope::engine(
919 EventType::MiddlewareBefore,
920 EventPhase::Completed,
921 event,
922 ))
923 }
924
925 fn working_status(&self, input: &DataEvent) -> Option<EventEnvelope> {
926 Some(EventEnvelope::engine(
927 EventType::MiddlewareBefore,
928 EventPhase::Started,
929 DataMiddlewareEvent::from(input),
930 ))
931 }
932
933 fn error_status(&self, input: &DataEvent, err: &Error) -> Option<EventEnvelope> {
934 Some(EventEnvelope::engine_error(
935 EventType::MiddlewareBefore,
936 EventPhase::Failed,
937 DataMiddlewareEvent::from(input),
938 err,
939 ))
940 }
941
942 fn retry_status(&self, input: &DataEvent, retry_policy: &RetryPolicy) -> Option<EventEnvelope> {
943 Some(EventEnvelope::engine(
944 EventType::MiddlewareBefore,
945 EventPhase::Retry,
946 json!({
947 "data": DataMiddlewareEvent::from(input),
948 "retry_count": retry_policy.current_retry,
949 "reason": retry_policy.reason.clone().unwrap_or_default(),
950 }),
951 ))
952 }
953}
954
955pub struct DataStoreProcessor {
956 middleware_manager: Arc<MiddlewareManager>,
957 outcomes: Arc<crate::engine::runner::StageCounter>,
966}
967#[async_trait]
968impl ProcessorTrait<DataEvent, ()> for DataStoreProcessor {
969 fn name(&self) -> &'static str {
970 "DataStoreProcessor"
971 }
972 async fn process(&self, input: DataEvent, context: ProcessorContext) -> ProcessorResult<()> {
977 info!(
978 "[DataStoreProcessor] start store: request_id={} account={} platform={} module={} size={}",
979 input.request_id,
980 input.account,
981 input.platform,
982 input.module,
983 input.size()
984 );
985 let mut middleware = vec![];
986 if let Some(retry_policy) = &context.retry_policy
987 && let Some(m_val) = retry_policy.meta.get("middleware")
988 && let Some(m) = m_val.as_array()
989 {
990 middleware = m
991 .iter()
992 .filter_map(|x| x.as_str())
993 .map(|x| x.to_string())
994 .collect::<Vec<String>>();
995 }
996 let config = context
997 .metadata
998 .read()
999 .await
1000 .get("config")
1001 .map(|x| {
1002 serde_json::from_value::<ModuleConfig>(x.clone()).unwrap_or_else(|e| {
1003 warn!(
1004 "[DataStoreProcessor] config conversion failed, using default: request_id={} module={} error={}",
1005 input.request_id,
1006 input.module,
1007 e
1008 );
1009 ModuleConfig::default()
1010 })
1011 });
1012 let request_id = input.request_id;
1013 let res = if middleware.is_empty() {
1014 self.middleware_manager
1015 .handle_store_data(input, &config)
1016 .await
1017 } else {
1018 self.middleware_manager
1019 .handle_store_data_with_middleware(input, middleware, &config)
1020 .await
1021 };
1022 if res.is_empty() {
1023 self.outcomes.record_success();
1024 info!(
1025 "[DataStoreProcessor] store success, request_id={}",
1026 request_id
1027 );
1028 ProcessorResult::Success(())
1029 } else {
1030 self.outcomes.record_failure();
1031 let error_msg = res
1032 .iter()
1033 .map(|(m, e)| format!("Middleware: {m}, Error: {e:?}"))
1034 .collect::<Vec<String>>()
1035 .join("; ");
1036 let mut retry_policy = context
1037 .retry_policy
1038 .unwrap_or_default()
1039 .with_reason(error_msg);
1040 let error_middleware = res.keys().map(|x| x.to_string()).collect::<Vec<String>>();
1041 retry_policy.meta = serde_json::json!({ "middleware": error_middleware });
1042 warn!(
1043 "[DataStoreProcessor] request={}, store error, will retry: {}",
1044 request_id,
1045 retry_policy.reason.clone().unwrap_or_default()
1046 );
1047 ProcessorResult::RetryableFailure(retry_policy)
1048 }
1049 }
1050}
1051impl EventProcessorTrait<DataEvent, ()> for DataStoreProcessor {
1052 fn pre_status(&self, input: &DataEvent) -> Option<EventEnvelope> {
1053 Some(EventEnvelope::engine(
1054 EventType::DataStore,
1055 EventPhase::Started,
1056 DataStoreEvent::from(input),
1057 ))
1058 }
1059
1060 fn finish_status(&self, input: &DataEvent, _output: &()) -> Option<EventEnvelope> {
1061 Some(EventEnvelope::engine(
1062 EventType::DataStore,
1063 EventPhase::Completed,
1064 DataStoreEvent::from(input),
1065 ))
1066 }
1067
1068 fn working_status(&self, input: &DataEvent) -> Option<EventEnvelope> {
1069 Some(EventEnvelope::engine(
1070 EventType::DataStore,
1071 EventPhase::Started,
1072 DataStoreEvent::from(input),
1073 ))
1074 }
1075
1076 fn error_status(&self, input: &DataEvent, err: &Error) -> Option<EventEnvelope> {
1077 Some(EventEnvelope::engine_error(
1078 EventType::DataStore,
1079 EventPhase::Failed,
1080 DataStoreEvent::from(input),
1081 err,
1082 ))
1083 }
1084
1085 fn retry_status(&self, input: &DataEvent, retry_policy: &RetryPolicy) -> Option<EventEnvelope> {
1086 Some(EventEnvelope::engine(
1087 EventType::DataStore,
1088 EventPhase::Retry,
1089 json!({
1090 "data": DataStoreEvent::from(input),
1091 "retry_count": retry_policy.current_retry,
1092 "reason": retry_policy.reason.clone().unwrap_or_default(),
1093 }),
1094 ))
1095 }
1096}
1097
1098pub async fn create_parser_chain(
1107 state: Arc<PipelineContext>,
1108 #[allow(dead_code)] task_manager: Arc<TaskManager>,
1109 middleware_manager: Arc<MiddlewareManager>,
1110 queue_manager: Arc<QueueManager>,
1111 event_bus: Option<Arc<EventBus>>,
1112 cache_service: Arc<CacheService>,
1113 store_outcomes: Arc<crate::engine::runner::StageCounter>,
1114) -> EventAwareTypedChain<Response, Vec<()>> {
1115 let response_module_processor = ResponseModuleProcessor {
1116 task_manager: task_manager.clone(),
1117 cache_service: cache_service.clone(),
1118 state: state.clone(),
1119 config_cache: Arc::new(DashMap::new()),
1120 };
1121 let response_parser_processor = ResponseParserProcessor {
1122 task_manager: task_manager.clone(),
1123 queue_manager,
1124 state,
1125 cache_service: cache_service.clone(),
1126 event_bus: event_bus.clone(),
1127 };
1128 let data_middleware_processor = DataMiddlewareProcessor {
1129 middleware_manager: middleware_manager.clone(),
1130 };
1131 let data_store_processor = DataStoreProcessor {
1132 middleware_manager,
1133 outcomes: store_outcomes,
1134 };
1135
1136 EventAwareTypedChain::<Response, Response>::new(event_bus)
1137 .then::<(Response, Arc<Module>, Arc<ModuleConfig>, Option<LoginInfo>), _>(
1138 response_module_processor,
1139 )
1140 .then::<Vec<DataEvent>, _>(response_parser_processor)
1141 .then_map_vec_parallel_with_strategy_silent::<DataEvent, _>(
1142 data_middleware_processor,
1143 64,
1144 ErrorStrategy::Skip,
1145 )
1146 .then_map_vec_parallel_with_strategy_silent::<(), _>(
1147 data_store_processor,
1148 64,
1149 ErrorStrategy::Skip,
1150 )
1151}