1use std::collections::{HashMap, HashSet};
10use std::sync::atomic::{AtomicBool, Ordering};
11use std::sync::{Arc, Condvar, Mutex};
12use std::time::{Duration, SystemTime};
13
14use dex_protocol::dex::{
15 AttributeWrite, ChannelInfo, ChannelMessage as ProtoChannelMessage, ChannelMessageDeletion,
16 ChannelValues, ConditionResults, ConditionStatus, Context as ProtoContext, Kv, StepStreamWrite,
17 Value as ProtoValue, value,
18};
19
20use crate::persistence::PersistenceKind;
21use crate::registry::{RegisteredFlow, decode_instance, physical_name, validate_map_instance};
22use crate::value_mapper;
23use crate::worker_output::StepOutputEmitter;
24use crate::{
25 Attribute, AttributeMap, Channel, ChannelMap, ChannelMessage, FlowResult, HandlerError,
26 HandlerResult, Stream, Value,
27};
28
29#[derive(Clone, Copy, Eq, PartialEq)]
30pub(crate) enum InvocationMethod {
31 WaitFor,
32 Execute,
33 Rpc,
34}
35
36pub(crate) struct ContextInput {
37 pub(crate) method: InvocationMethod,
38 pub(crate) flow: RegisteredFlow,
39 pub(crate) metadata: ProtoContext,
40 pub(crate) attributes: Vec<Kv>,
41 pub(crate) locals: Vec<Kv>,
42 pub(crate) condition_results: Option<ConditionResults>,
43 pub(crate) channel_infos: HashMap<String, ChannelInfo>,
44 pub(crate) loaded_channel_messages: HashMap<String, ChannelValues>,
45 pub(crate) loaded_attribute_map_instances: Vec<String>,
46 pub(crate) loaded_channel_names: Vec<String>,
47 pub(crate) loaded_channel_map_instances: Vec<String>,
48}
49
50pub(crate) struct InvocationOutputs {
51 pub(crate) attributes: Vec<AttributeWrite>,
52 pub(crate) locals: Vec<Kv>,
53 pub(crate) events: Vec<Kv>,
54 pub(crate) publications: Vec<ProtoChannelMessage>,
55 pub(crate) channel_deletions: Vec<ChannelMessageDeletion>,
56}
57
58#[derive(Clone, Debug, Eq, PartialEq)]
72pub struct RecoveryErrorInfo {
73 detail: String,
74 error_type: String,
75}
76
77impl RecoveryErrorInfo {
78 pub fn detail(&self) -> &str {
80 &self.detail
81 }
82
83 pub fn error_type(&self) -> &str {
85 &self.error_type
86 }
87}
88
89pub struct Context {
95 method: InvocationMethod,
96 flow: RegisteredFlow,
97 output_emitter: Option<StepOutputEmitter>,
98 metadata: ProtoContext,
99 recovery_error: Option<RecoveryErrorInfo>,
100 attributes: HashMap<String, ProtoValue>,
101 locals: HashMap<String, ProtoValue>,
102 condition_results: Option<ConditionResults>,
103 channel_infos: HashMap<String, ChannelInfo>,
104 loaded_channel_messages: HashMap<String, ChannelValues>,
105 loaded_attribute_map_instances: HashSet<String>,
106 loaded_channel_names: HashSet<String>,
107 loaded_channel_map_instances: HashSet<String>,
108 attribute_writes: HashMap<String, AttributeWrite>,
109 local_writes: HashMap<String, Kv>,
110 events: Vec<Kv>,
111 event_names: HashSet<String>,
112 publications: Vec<ProtoChannelMessage>,
113 channel_deletions: Vec<ChannelMessageDeletion>,
114 cancellation: InvocationCancellation,
115 runtime: Option<tokio::runtime::Handle>,
116 step_output_finalizers: Vec<Arc<dyn StepOutputFinalizer>>,
117 step_outputs_finalized: bool,
118}
119
120pub(crate) trait StepOutputFinalizer: Send + Sync {
121 fn finalize_step_output(&self) -> HandlerResult<()>;
122}
123
124impl Context {
125 pub(crate) fn new(
126 input: ContextInput,
127 output_emitter: Option<StepOutputEmitter>,
128 cancellation: InvocationCancellation,
129 ) -> HandlerResult<Self> {
130 let recovery_error =
131 input
132 .metadata
133 .recovery_error
134 .as_ref()
135 .map(|error| RecoveryErrorInfo {
136 detail: error.detail.clone(),
137 error_type: error.error_type.clone(),
138 });
139 Ok(Self {
140 method: input.method,
141 flow: input.flow,
142 output_emitter,
143 metadata: input.metadata,
144 recovery_error,
145 attributes: map_values("Attribute", input.attributes)?,
146 locals: map_values("step-execution local", input.locals)?,
147 condition_results: input.condition_results,
148 channel_infos: input.channel_infos,
149 loaded_channel_messages: input.loaded_channel_messages,
150 loaded_attribute_map_instances: input
151 .loaded_attribute_map_instances
152 .into_iter()
153 .collect(),
154 loaded_channel_names: input.loaded_channel_names.into_iter().collect(),
155 loaded_channel_map_instances: input.loaded_channel_map_instances.into_iter().collect(),
156 attribute_writes: HashMap::new(),
157 local_writes: HashMap::new(),
158 events: Vec::new(),
159 event_names: HashSet::new(),
160 publications: Vec::new(),
161 channel_deletions: Vec::new(),
162 cancellation,
163 runtime: tokio::runtime::Handle::try_current().ok(),
164 step_output_finalizers: Vec::new(),
165 step_outputs_finalized: false,
166 })
167 }
168
169 pub fn flow_id(&self) -> &str {
171 &self.metadata.flow_id
172 }
173
174 pub fn run_id(&self) -> &str {
176 &self.metadata.run_id
177 }
178
179 pub fn flow_started_at(&self) -> SystemTime {
181 system_time(self.metadata.flow_started_timestamp)
182 }
183
184 pub fn step_execution_id(&self) -> &str {
186 &self.metadata.step_execution_id
187 }
188
189 pub fn from_step_execution_id(&self) -> &str {
191 &self.metadata.from_step_execution_id
192 }
193
194 pub fn recovery_error(&self) -> Option<&RecoveryErrorInfo> {
196 self.recovery_error.as_ref()
197 }
198
199 pub fn first_attempt_at(&self) -> SystemTime {
201 system_time(self.metadata.first_attempt_timestamp)
202 }
203
204 pub fn attempt(&self) -> u32 {
206 u32::try_from(self.metadata.attempt).unwrap_or_default()
207 }
208
209 pub fn record_heartbeat(&mut self) -> HandlerResult<()> {
219 self.step_output_emitter()?.send_heartbeat(None)
220 }
221
222 pub fn record_heartbeat_value<T: Value>(&mut self, value: T) -> HandlerResult<()> {
251 let value = value_mapper::encode_handler(&value)?;
252 self.step_output_emitter()?.send_heartbeat(Some(value))
253 }
254
255 pub fn last_heartbeat_value<T: Value>(&self) -> HandlerResult<Option<T>> {
266 if self.method == InvocationMethod::Rpc {
267 return Err(HandlerError::new(
268 "dex_sdk::HandlerError",
269 "Heartbeat values require a Step Context",
270 ));
271 }
272 self.metadata
273 .last_heartbeat_value
274 .as_ref()
275 .map(value_mapper::decode_handler)
276 .transpose()
277 }
278
279 pub fn has_any_timer_fired(&self) -> bool {
281 self.condition_results.as_ref().is_some_and(|results| {
282 results
283 .timer_results
284 .iter()
285 .any(|timer| timer.condition_status == ConditionStatus::Completed as i32)
286 })
287 }
288
289 pub fn has_timer_fired(&self, index: usize) -> bool {
293 self.condition_results
294 .as_ref()
295 .and_then(|results| results.timer_results.get(index))
296 .is_some_and(|timer| timer.condition_status == ConditionStatus::Completed as i32)
297 }
298
299 pub fn wait_for_method_failed(&self) -> bool {
301 self.condition_results
302 .as_ref()
303 .is_some_and(|results| results.wait_for_failed)
304 }
305
306 pub(crate) fn sub_flow_result(&self, index: usize) -> HandlerResult<FlowResult> {
307 if self.method != InvocationMethod::Execute {
308 return Err(HandlerError::new(
309 "dex_sdk::HandlerError",
310 "SubFlow results are available only during execute",
311 ));
312 }
313 let result = self
314 .condition_results
315 .as_ref()
316 .and_then(|results| results.sub_flow_results.get(index))
317 .ok_or_else(|| {
318 HandlerError::new(
319 "dex_sdk::HandlerError",
320 format!("SubFlow result index is unavailable: {index}"),
321 )
322 })?;
323 FlowResult::from_proto(result).map_err(HandlerError::from_error)
324 }
325
326 pub(crate) fn sub_flow_id(&self, index: usize) -> HandlerResult<String> {
327 self.sub_flow_result(index)?;
328 Ok(format!(
329 "SubFlow:{}-{}-{index}",
330 self.flow_id(),
331 self.step_execution_id()
332 ))
333 }
334
335 pub fn wait_for_cancellation(&self) {
339 self.cancellation.wait();
340 }
341
342 pub fn is_cancelled(&self) -> bool {
344 self.cancellation.is_cancelled()
345 }
346
347 pub fn set_step_execution_local<T: Value>(&mut self, key: &str, value: T) -> HandlerResult<()> {
353 require_name(key, "step-execution local key")?;
354 let value = value_mapper::encode_handler(&value)?;
355 self.local_writes.insert(
356 key.to_string(),
357 Kv {
358 key: key.to_string(),
359 value: Some(value),
360 },
361 );
362 Ok(())
363 }
364
365 pub fn step_execution_local<T: Value>(&self, key: &str) -> HandlerResult<T> {
371 require_name(key, "step-execution local key")?;
372 let value = self
373 .local_writes
374 .get(key)
375 .and_then(|entry| entry.value.as_ref())
376 .or_else(|| self.locals.get(key))
377 .ok_or_else(|| {
378 HandlerError::new(
379 "dex_sdk::HandlerError",
380 format!("step-execution local {key} is missing"),
381 )
382 })?;
383 value_mapper::decode_handler(value)
384 }
385
386 pub fn record_event<T: Value>(&mut self, name: &str, value: T) -> HandlerResult<()> {
394 require_name(name, "event name")?;
395 if !self.event_names.insert(name.to_string()) {
396 return Err(HandlerError::new(
397 "dex_sdk::HandlerError",
398 format!("event was already recorded: {name}"),
399 ));
400 }
401 self.events.push(Kv {
402 key: name.to_string(),
403 value: Some(value_mapper::encode_handler(&value)?),
404 });
405 Ok(())
406 }
407
408 pub fn get_attribute<T: Value>(&self, attribute: &Attribute<T>) -> HandlerResult<Option<T>> {
410 self.get_attribute_value(attribute.name(), PersistenceKind::Attribute, None)
411 }
412
413 pub fn get_attribute_map<T: Value>(
415 &self,
416 attribute: &AttributeMap<T>,
417 instance: &str,
418 ) -> HandlerResult<Option<T>> {
419 self.get_attribute_value(
420 attribute.name(),
421 PersistenceKind::AttributeMap,
422 Some(instance),
423 )
424 }
425
426 pub fn set_attribute<T: Value>(
428 &mut self,
429 attribute: &Attribute<T>,
430 value: T,
431 ) -> HandlerResult<()> {
432 self.set_attribute_value(
433 attribute.name(),
434 PersistenceKind::Attribute,
435 None,
436 value_mapper::encode_handler(&value)?,
437 attribute.index().map(|index| index.proto_config(false)),
438 attribute.sync_config(),
439 )
440 }
441
442 pub fn set_attribute_map<T: Value>(
444 &mut self,
445 attribute: &AttributeMap<T>,
446 instance: &str,
447 value: T,
448 ) -> HandlerResult<()> {
449 self.set_attribute_value(
450 attribute.name(),
451 PersistenceKind::AttributeMap,
452 Some(instance),
453 value_mapper::encode_handler(&value)?,
454 attribute.index().map(|index| index.proto_config(true)),
455 attribute.sync_config(),
456 )
457 }
458
459 pub fn delete_attribute<T>(&mut self, attribute: &Attribute<T>) -> HandlerResult<()> {
461 self.set_attribute_value(
462 attribute.name(),
463 PersistenceKind::Attribute,
464 None,
465 value_mapper::deletion(),
466 attribute.index().map(|index| index.proto_config(false)),
467 attribute.sync_config(),
468 )
469 }
470
471 pub fn delete_attribute_map<T>(
473 &mut self,
474 attribute: &AttributeMap<T>,
475 instance: &str,
476 ) -> HandlerResult<()> {
477 self.set_attribute_value(
478 attribute.name(),
479 PersistenceKind::AttributeMap,
480 Some(instance),
481 value_mapper::deletion(),
482 attribute.index().map(|index| index.proto_config(true)),
483 attribute.sync_config(),
484 )
485 }
486
487 pub(crate) fn attribute_map_keys<T>(
488 &self,
489 attribute: &AttributeMap<T>,
490 ) -> HandlerResult<Vec<String>> {
491 self.registered_name(
492 attribute.name(),
493 PersistenceKind::AttributeMap,
494 Some("key-check"),
495 )?;
496 if !self
497 .loaded_attribute_map_instances
498 .contains(&format!("{}/", attribute.name()))
499 {
500 return Err(attribute_map_not_loaded(format!(
501 "all AttributeMap instances were not loaded for this invocation: {}",
502 attribute.name()
503 )));
504 }
505 let prefix = format!("{}/", attribute.name());
506 let mut physical_keys = self
507 .attributes
508 .keys()
509 .filter(|key| key.starts_with(&prefix))
510 .cloned()
511 .collect::<HashSet<_>>();
512 for (key, write) in &self.attribute_writes {
513 if !key.starts_with(&prefix) {
514 continue;
515 }
516 if matches!(
517 write.value.as_ref().and_then(|value| value.kind.as_ref()),
518 Some(value::Kind::NullValue(_))
519 ) {
520 physical_keys.remove(key);
521 } else {
522 physical_keys.insert(key.clone());
523 }
524 }
525 sorted_instance_keys(&prefix, physical_keys)
526 }
527
528 pub fn publish<T: Value>(&mut self, channel: &Channel<T>, value: T) -> HandlerResult<()> {
530 self.publish_value(
531 channel.name(),
532 PersistenceKind::Channel,
533 None,
534 value_mapper::encode_handler(&value)?,
535 )
536 }
537
538 pub fn publish_map<T: Value>(
540 &mut self,
541 channel: &ChannelMap<T>,
542 instance: &str,
543 value: T,
544 ) -> HandlerResult<()> {
545 self.publish_value(
546 channel.name(),
547 PersistenceKind::ChannelMap,
548 Some(instance),
549 value_mapper::encode_handler(&value)?,
550 )
551 }
552
553 pub fn channel_size<T>(&self, channel: &Channel<T>) -> HandlerResult<usize> {
555 self.channel_size_value(channel.name(), PersistenceKind::Channel, None)
556 }
557
558 pub fn channel_map_size<T>(
560 &self,
561 channel: &ChannelMap<T>,
562 instance: &str,
563 ) -> HandlerResult<usize> {
564 self.channel_size_value(channel.name(), PersistenceKind::ChannelMap, Some(instance))
565 }
566
567 pub fn pending_channel_messages<T: Value>(
569 &self,
570 channel: &Channel<T>,
571 ) -> HandlerResult<Vec<ChannelMessage<T>>> {
572 self.pending_channel_messages_value(channel.name(), PersistenceKind::Channel, None)
573 }
574
575 pub fn pending_channel_map_messages<T: Value>(
577 &self,
578 channel: &ChannelMap<T>,
579 instance: &str,
580 ) -> HandlerResult<Vec<ChannelMessage<T>>> {
581 self.pending_channel_messages_value(
582 channel.name(),
583 PersistenceKind::ChannelMap,
584 Some(instance),
585 )
586 }
587
588 pub(crate) fn channel_map_keys<T>(
589 &self,
590 channel: &ChannelMap<T>,
591 ) -> HandlerResult<Vec<String>> {
592 self.registered_name(
593 channel.name(),
594 PersistenceKind::ChannelMap,
595 Some("key-check"),
596 )?;
597 let prefix = format!("{}/", channel.name());
598 let physical_keys = self
599 .channel_infos
600 .iter()
601 .filter(|(key, info)| key.starts_with(&prefix) && info.size > 0)
602 .map(|(key, _)| key.clone())
603 .collect::<HashSet<_>>();
604 sorted_instance_keys(&prefix, physical_keys)
605 }
606
607 pub fn channel_results<T: Value>(&self, channel: &Channel<T>) -> HandlerResult<Vec<T>> {
609 self.channel_results_value(channel.name(), PersistenceKind::Channel, None)
610 }
611
612 pub fn channel_map_results<T: Value>(
614 &self,
615 channel: &ChannelMap<T>,
616 instance: &str,
617 ) -> HandlerResult<Vec<T>> {
618 self.channel_results_value(channel.name(), PersistenceKind::ChannelMap, Some(instance))
619 }
620
621 pub fn write_stream<T: Value>(&mut self, stream: &Stream<T>, value: T) -> HandlerResult<()> {
632 let output_emitter = self.step_output_emitter()?.clone();
633 let definition = self
634 .flow
635 .persistence
636 .get(stream.name())
637 .filter(|definition| {
638 definition.kind == PersistenceKind::Stream
639 && definition.stream_identity == Some(stream.identity())
640 })
641 .ok_or_else(|| {
642 HandlerError::new(
643 "dex_sdk::HandlerError",
644 format!("Stream does not belong to Flow: {}", stream.name()),
645 )
646 })?;
647 if definition.stream_capacity_bytes != Some(stream.stream_capacity_bytes()) {
648 return Err(HandlerError::new(
649 "dex_sdk::HandlerError",
650 format!(
651 "Stream capacity does not match its registered definition: {}",
652 stream.name()
653 ),
654 ));
655 }
656 output_emitter.send_stream_write(StepStreamWrite {
657 stream_name: stream.name().to_string(),
658 stream_capacity_bytes: stream.stream_capacity_bytes(),
659 value: Some(value_mapper::encode_handler(&value)?),
660 })
661 }
662
663 pub(crate) fn prepare_buffered_text_stream(
664 &self,
665 stream: &Stream<String>,
666 ) -> HandlerResult<(
667 StepOutputEmitter,
668 InvocationCancellation,
669 tokio::runtime::Handle,
670 )> {
671 let output_emitter = self.step_output_emitter()?.clone();
672 let definition = self
673 .flow
674 .persistence
675 .get(stream.name())
676 .filter(|definition| {
677 definition.kind == PersistenceKind::Stream
678 && definition.stream_identity == Some(stream.identity())
679 })
680 .ok_or_else(|| {
681 HandlerError::new(
682 "dex_sdk::HandlerError",
683 format!("Stream does not belong to Flow: {}", stream.name()),
684 )
685 })?;
686 if definition.stream_capacity_bytes != Some(stream.stream_capacity_bytes()) {
687 return Err(HandlerError::new(
688 "dex_sdk::HandlerError",
689 format!(
690 "Stream capacity does not match its registered definition: {}",
691 stream.name()
692 ),
693 ));
694 }
695 let runtime = self.runtime.clone().ok_or_else(|| {
696 HandlerError::new(
697 "dex_sdk::HandlerError",
698 "Buffered Stream runtime is unavailable",
699 )
700 })?;
701 Ok((output_emitter, self.cancellation.clone(), runtime))
702 }
703
704 pub(crate) fn register_step_output_finalizer(
705 &mut self,
706 finalizer: Arc<dyn StepOutputFinalizer>,
707 ) -> HandlerResult<()> {
708 if self.step_outputs_finalized {
709 return Err(HandlerError::new(
710 "dex_sdk::HandlerError",
711 "Buffered Stream invocation has finished",
712 ));
713 }
714 self.step_output_finalizers.push(finalizer);
715 Ok(())
716 }
717
718 pub(crate) fn finalize_step_outputs<T>(
719 &mut self,
720 result: HandlerResult<T>,
721 ) -> HandlerResult<T> {
722 if self.step_outputs_finalized {
723 return result;
724 }
725 self.step_outputs_finalized = true;
726 let mut result = result;
727 for finalizer in &self.step_output_finalizers {
728 if let Err(finalization_failure) = finalizer.finalize_step_output() {
729 match &mut result {
730 Ok(_) => result = Err(finalization_failure),
731 Err(handler_failure) => {
732 handler_failure.attach_finalizer_error(&finalization_failure);
733 }
734 }
735 }
736 }
737 result
738 }
739
740 pub(crate) fn cancellation(&self) -> InvocationCancellation {
741 self.cancellation.clone()
742 }
743
744 pub(crate) fn take_outputs(self) -> InvocationOutputs {
745 InvocationOutputs {
746 attributes: self.attribute_writes.into_values().collect(),
747 locals: self.local_writes.into_values().collect(),
748 events: self.events,
749 publications: self.publications,
750 channel_deletions: self.channel_deletions,
751 }
752 }
753
754 fn step_output_emitter(&self) -> HandlerResult<&StepOutputEmitter> {
755 if self.method == InvocationMethod::Rpc {
756 return Err(HandlerError::new(
757 "dex_sdk::HandlerError",
758 "Step progress requires a Step Context",
759 ));
760 }
761 if self.cancellation.is_cancelled() {
762 return Err(HandlerError::new(
763 "dex_sdk::HandlerError",
764 "Step invocation is canceled",
765 ));
766 }
767 self.output_emitter.as_ref().ok_or_else(|| {
768 HandlerError::new("dex_sdk::HandlerError", "Step output stream is unavailable")
769 })
770 }
771
772 fn get_attribute_value<T: Value>(
773 &self,
774 name: &str,
775 kind: PersistenceKind,
776 instance: Option<&str>,
777 ) -> HandlerResult<Option<T>> {
778 let key = self.registered_name(name, kind, instance)?;
779 if kind == PersistenceKind::AttributeMap {
780 let is_loaded = self
781 .loaded_attribute_map_instances
782 .contains(&format!("{name}/"))
783 || self.loaded_attribute_map_instances.contains(&key);
784 if !is_loaded {
785 return Err(attribute_map_not_loaded(format!(
786 "AttributeMap instance was not loaded for this invocation: {key}"
787 )));
788 }
789 }
790 if let Some(write) = self.attribute_writes.get(&key) {
791 let value = write.value.as_ref().ok_or_else(|| {
792 HandlerError::new(
793 "dex_sdk::HandlerError",
794 format!("Attribute write {key} has no Value"),
795 )
796 })?;
797 if matches!(value.kind, Some(value::Kind::NullValue(_))) {
798 return Ok(None);
799 }
800 return value_mapper::decode_handler(value).map(Some);
801 }
802 self.attributes
803 .get(&key)
804 .map(value_mapper::decode_handler)
805 .transpose()
806 }
807
808 fn set_attribute_value(
809 &mut self,
810 name: &str,
811 kind: PersistenceKind,
812 instance: Option<&str>,
813 value: ProtoValue,
814 index_config: Option<dex_protocol::dex::IndexConfig>,
815 sync_config: Option<dex_protocol::dex::AttributeSyncConfig>,
816 ) -> HandlerResult<()> {
817 let key = self.registered_name(name, kind, instance)?;
818 self.attribute_writes.insert(
819 key.clone(),
820 AttributeWrite {
821 key,
822 value: Some(value),
823 index_config,
824 sync_config,
825 },
826 );
827 Ok(())
828 }
829
830 fn publish_value(
831 &mut self,
832 name: &str,
833 kind: PersistenceKind,
834 instance: Option<&str>,
835 value: ProtoValue,
836 ) -> HandlerResult<()> {
837 let channel_name = self.registered_name(name, kind, instance)?;
838 self.publications.push(ProtoChannelMessage {
839 channel_name: channel_name.clone(),
840 value: Some(value),
841 message_id: String::new(),
842 });
843 self.channel_infos
844 .entry(channel_name)
845 .and_modify(|info| info.size += 1)
846 .or_insert(ChannelInfo { size: 1 });
847 Ok(())
848 }
849
850 pub fn delete_channel_message<T>(
852 &mut self,
853 channel: &Channel<T>,
854 message_id: &str,
855 ) -> HandlerResult<()> {
856 self.delete_channel_message_value(
857 channel.name(),
858 PersistenceKind::Channel,
859 None,
860 message_id,
861 )
862 }
863
864 pub fn delete_channel_map_message<T>(
866 &mut self,
867 channel: &ChannelMap<T>,
868 instance: &str,
869 message_id: &str,
870 ) -> HandlerResult<()> {
871 self.delete_channel_message_value(
872 channel.name(),
873 PersistenceKind::ChannelMap,
874 Some(instance),
875 message_id,
876 )
877 }
878
879 fn delete_channel_message_value(
880 &mut self,
881 name: &str,
882 kind: PersistenceKind,
883 instance: Option<&str>,
884 message_id: &str,
885 ) -> HandlerResult<()> {
886 if message_id.is_empty() {
887 return Err(HandlerError::new(
888 "dex_sdk::HandlerError",
889 "Channel message ID must not be empty",
890 ));
891 }
892 let channel_name = self.registered_name(name, kind, instance)?;
893 self.channel_deletions.push(ChannelMessageDeletion {
894 channel_name: channel_name.clone(),
895 message_id: message_id.to_string(),
896 });
897 if let Some(info) = self.channel_infos.get_mut(&channel_name) {
898 info.size = info.size.saturating_sub(1);
899 }
900 Ok(())
901 }
902
903 fn channel_size_value(
904 &self,
905 name: &str,
906 kind: PersistenceKind,
907 instance: Option<&str>,
908 ) -> HandlerResult<usize> {
909 let channel_name = self.registered_name(name, kind, instance)?;
910 Ok(self
911 .channel_infos
912 .get(&channel_name)
913 .map_or(0, |info| usize::try_from(info.size).unwrap_or_default()))
914 }
915
916 fn channel_results_value<T: Value>(
917 &self,
918 name: &str,
919 kind: PersistenceKind,
920 instance: Option<&str>,
921 ) -> HandlerResult<Vec<T>> {
922 let channel_name = self.registered_name(name, kind, instance)?;
923 let Some(results) = &self.condition_results else {
924 return Ok(Vec::new());
925 };
926 let mut decoded = Vec::new();
927 for result in &results.channel_results {
928 if result.channel_name == channel_name
929 && result.condition_status == ConditionStatus::Completed as i32
930 {
931 for value in &result.values {
932 decoded.push(value_mapper::decode_handler(value)?);
933 }
934 }
935 }
936 Ok(decoded)
937 }
938
939 fn pending_channel_messages_value<T: Value>(
940 &self,
941 name: &str,
942 kind: PersistenceKind,
943 instance: Option<&str>,
944 ) -> HandlerResult<Vec<ChannelMessage<T>>> {
945 let physical_name = self.registered_name(name, kind, instance)?;
946 let is_loaded = match kind {
947 PersistenceKind::Channel => self.loaded_channel_names.contains(&physical_name),
948 PersistenceKind::ChannelMap => {
949 self.loaded_channel_map_instances
950 .contains(&format!("{name}/"))
951 || self.loaded_channel_map_instances.contains(&physical_name)
952 }
953 _ => false,
954 };
955 if !is_loaded {
956 return Err(channel_messages_not_loaded(format!(
957 "Channel messages were not loaded for this invocation: {physical_name}"
958 )));
959 }
960 self.loaded_channel_messages
961 .get(&physical_name)
962 .map_or(&[][..], |values| values.messages.as_slice())
963 .iter()
964 .map(|message| {
965 let value = message.value.as_ref().ok_or_else(|| {
966 HandlerError::new(
967 "dex_sdk::HandlerError",
968 format!("Channel message {} has no Value", message.message_id),
969 )
970 })?;
971 Ok(ChannelMessage {
972 message_id: message.message_id.clone(),
973 value: value_mapper::decode_handler(value)?,
974 })
975 })
976 .collect()
977 }
978
979 fn registered_name(
980 &self,
981 name: &str,
982 kind: PersistenceKind,
983 instance: Option<&str>,
984 ) -> HandlerResult<String> {
985 let definition = self.flow.persistence.get(name).ok_or_else(|| {
986 HandlerError::new(
987 "dex_sdk::HandlerError",
988 format!("persistence definition does not belong to Flow: {name}"),
989 )
990 })?;
991 if definition.kind != kind {
992 return Err(HandlerError::new(
993 "dex_sdk::HandlerError",
994 format!("persistence definition kind does not match: {name}"),
995 ));
996 }
997 match kind {
998 PersistenceKind::AttributeMap | PersistenceKind::ChannelMap => {
999 let instance = instance.ok_or_else(|| {
1000 HandlerError::new(
1001 "dex_sdk::HandlerError",
1002 format!("{name} requires an instance"),
1003 )
1004 })?;
1005 validate_map_instance(instance)
1006 .map_err(|error| HandlerError::new("dex_sdk::HandlerError", error))?;
1007 Ok(physical_name(name, instance))
1008 }
1009 PersistenceKind::Attribute | PersistenceKind::Channel if instance.is_none() => {
1010 Ok(name.to_string())
1011 }
1012 _ => Err(HandlerError::new(
1013 "dex_sdk::HandlerError",
1014 format!("static persistence definition cannot use an instance: {name}"),
1015 )),
1016 }
1017 }
1018}
1019
1020fn attribute_map_not_loaded(message: impl Into<String>) -> HandlerError {
1021 HandlerError::new("dex_sdk::AttributeMapNotLoadedError", message)
1022}
1023
1024fn channel_messages_not_loaded(message: impl Into<String>) -> HandlerError {
1025 HandlerError::new("dex_sdk::ChannelMessagesNotLoadedError", message)
1026}
1027
1028fn sorted_instance_keys(
1029 prefix: &str,
1030 physical_keys: HashSet<String>,
1031) -> HandlerResult<Vec<String>> {
1032 let mut keys = physical_keys
1033 .iter()
1034 .map(|key| {
1035 decode_instance(key, prefix)
1036 .map_err(|error| HandlerError::new("dex_sdk::HandlerError", error))
1037 })
1038 .collect::<HandlerResult<Vec<_>>>()?;
1039 keys.sort();
1040 Ok(keys)
1041}
1042
1043#[derive(Clone)]
1044pub(crate) struct InvocationCancellation {
1045 inner: Arc<InvocationCancellationInner>,
1046}
1047
1048struct InvocationCancellationInner {
1049 cancelled: AtomicBool,
1050 mutex: Mutex<()>,
1051 condition: Condvar,
1052 notification: tokio::sync::Notify,
1053}
1054
1055impl InvocationCancellation {
1056 pub(crate) fn new() -> Self {
1057 Self {
1058 inner: Arc::new(InvocationCancellationInner {
1059 cancelled: AtomicBool::new(false),
1060 mutex: Mutex::new(()),
1061 condition: Condvar::new(),
1062 notification: tokio::sync::Notify::new(),
1063 }),
1064 }
1065 }
1066
1067 fn wait(&self) {
1068 let mut guard = self.inner.mutex.lock().expect("cancellation lock");
1069 while !self.is_cancelled() {
1070 guard = self.inner.condition.wait(guard).expect("cancellation lock");
1071 }
1072 }
1073
1074 pub(crate) fn is_cancelled(&self) -> bool {
1075 self.inner.cancelled.load(Ordering::Acquire)
1076 }
1077
1078 pub(crate) fn cancel(&self) {
1079 let _guard = self.inner.mutex.lock().expect("cancellation lock");
1080 self.inner.cancelled.store(true, Ordering::Release);
1081 self.inner.condition.notify_all();
1082 self.inner.notification.notify_waiters();
1083 }
1084
1085 pub(crate) async fn cancelled(&self) {
1086 let notified = self.inner.notification.notified();
1087 if self.is_cancelled() {
1088 return;
1089 }
1090 notified.await;
1091 }
1092}
1093
1094fn system_time(timestamp: i64) -> SystemTime {
1095 if timestamp >= 0 {
1096 SystemTime::UNIX_EPOCH + Duration::from_secs(timestamp as u64)
1097 } else {
1098 SystemTime::UNIX_EPOCH - Duration::from_secs(timestamp.unsigned_abs())
1099 }
1100}
1101
1102fn map_values(kind: &str, entries: Vec<Kv>) -> HandlerResult<HashMap<String, ProtoValue>> {
1103 let mut mapped = HashMap::new();
1104 for entry in entries {
1105 require_name(&entry.key, kind)?;
1106 let value = entry.value.ok_or_else(|| {
1107 HandlerError::new(
1108 "dex_sdk::HandlerError",
1109 format!("{kind} {} has no Value", entry.key),
1110 )
1111 })?;
1112 if mapped.insert(entry.key.clone(), value).is_some() {
1113 return Err(HandlerError::new(
1114 "dex_sdk::HandlerError",
1115 format!("duplicate {kind} {}", entry.key),
1116 ));
1117 }
1118 }
1119 Ok(mapped)
1120}
1121
1122fn require_name(value: &str, kind: &str) -> HandlerResult<()> {
1123 if value.is_empty() {
1124 Err(HandlerError::new(
1125 "dex_sdk::HandlerError",
1126 format!("{kind} is required"),
1127 ))
1128 } else {
1129 Ok(())
1130 }
1131}
1132
1133#[cfg(test)]
1134mod tests {
1135 use super::{Context, ContextInput, InvocationCancellation, InvocationMethod};
1136 use crate::{
1137 AttributeMap, Channel, ChannelMap, Flow, PersistenceSchema, Registry,
1138 registry::physical_name, value_mapper,
1139 };
1140 use dex_protocol::dex::{
1141 ChannelInfo, ChannelMessage as ProtoChannelMessage, ChannelValues, Context as ProtoContext,
1142 Kv,
1143 };
1144 use std::collections::HashMap;
1145
1146 struct MapFlow {
1147 attributes: AttributeMap<String>,
1148 channels: ChannelMap<String>,
1149 }
1150
1151 impl Flow for MapFlow {
1152 type StartInput = ();
1153
1154 fn persistence(&self) -> PersistenceSchema {
1155 PersistenceSchema::new()
1156 .attribute_map(&self.attributes)
1157 .channel_map(&self.channels)
1158 }
1159 }
1160
1161 #[test]
1162 fn map_introspection_tracks_buffered_changes() {
1163 let flow = MapFlow {
1164 attributes: AttributeMap::new("items"),
1165 channels: ChannelMap::new("messages"),
1166 };
1167 let registry = Registry::new().register(flow).expect("register map Flow");
1168 let registered = registry.flow("MapFlow").expect("lookup map Flow").clone();
1169 let attributes = AttributeMap::<String>::new("items");
1170 let channels = ChannelMap::<String>::new("messages");
1171 let special = "special % key";
1172 let mut context = Context::new(
1173 ContextInput {
1174 method: InvocationMethod::Rpc,
1175 flow: registered,
1176 metadata: ProtoContext::default(),
1177 attributes: vec![
1178 Kv {
1179 key: physical_name("items", special),
1180 value: Some(value_mapper::encode_handler(&"initial".to_string()).unwrap()),
1181 },
1182 Kv {
1183 key: physical_name("items", "z"),
1184 value: Some(value_mapper::encode_handler(&"remove".to_string()).unwrap()),
1185 },
1186 ],
1187 locals: Vec::new(),
1188 condition_results: None,
1189 channel_infos: HashMap::from([
1190 (physical_name("messages", special), ChannelInfo { size: 1 }),
1191 (physical_name("messages", "empty"), ChannelInfo { size: 0 }),
1192 ]),
1193 loaded_channel_messages: HashMap::new(),
1194 loaded_attribute_map_instances: vec!["items/".to_string()],
1195 loaded_channel_names: Vec::new(),
1196 loaded_channel_map_instances: Vec::new(),
1197 },
1198 None,
1199 InvocationCancellation::new(),
1200 )
1201 .expect("create RPC Context");
1202
1203 assert!(
1204 attributes
1205 .set(&mut context, "tenant/order", "invalid".to_string())
1206 .is_err()
1207 );
1208 assert!(
1209 channels
1210 .publish(&mut context, "tenant/order", "invalid".to_string())
1211 .is_err()
1212 );
1213
1214 assert_eq!(
1215 vec![special.to_string(), "z".to_string()],
1216 attributes.all_instance_keys(&context).unwrap()
1217 );
1218 attributes
1219 .set(&mut context, "a", "added".to_string())
1220 .unwrap();
1221 attributes.delete(&mut context, "z").unwrap();
1222 assert_eq!(
1223 vec!["a".to_string(), special.to_string()],
1224 attributes.all_instance_keys(&context).unwrap()
1225 );
1226 assert_eq!(2, attributes.map_size(&context).unwrap());
1227
1228 assert_eq!(
1229 vec![special.to_string()],
1230 channels.all_instance_keys(&context).unwrap()
1231 );
1232 channels
1233 .publish(&mut context, "a", "published".to_string())
1234 .unwrap();
1235 assert_eq!(
1236 vec!["a".to_string(), special.to_string()],
1237 channels.all_instance_keys(&context).unwrap()
1238 );
1239 assert_eq!(2, channels.map_size(&context).unwrap());
1240 assert!(context.record_heartbeat().is_err());
1241 assert!(
1242 context
1243 .record_heartbeat_value("checkpoint".to_string())
1244 .is_err()
1245 );
1246 assert!(context.last_heartbeat_value::<String>().is_err());
1247 }
1248
1249 struct SelectiveFlow {
1250 attributes: AttributeMap<String>,
1251 queued: Channel<String>,
1252 by_tenant: ChannelMap<String>,
1253 }
1254
1255 impl Flow for SelectiveFlow {
1256 type StartInput = ();
1257
1258 fn persistence(&self) -> PersistenceSchema {
1259 PersistenceSchema::new()
1260 .attribute_map(&self.attributes)
1261 .channel(&self.queued)
1262 .channel_map(&self.by_tenant)
1263 }
1264 }
1265
1266 #[test]
1267 fn rpc_selective_state_snapshots_are_typed_and_presence_aware() {
1268 let flow = SelectiveFlow {
1269 attributes: AttributeMap::new("selected-items"),
1270 queued: Channel::new("selected-commands"),
1271 by_tenant: ChannelMap::new("selected-by-tenant"),
1272 };
1273 let registry = Registry::new()
1274 .register(flow)
1275 .expect("register selective Flow");
1276 let registered = registry
1277 .flow("SelectiveFlow")
1278 .expect("lookup selective Flow")
1279 .clone();
1280 let attributes = AttributeMap::<String>::new("selected-items");
1281 let queued = Channel::<String>::new("selected-commands");
1282 let by_tenant = ChannelMap::<String>::new("selected-by-tenant");
1283 let attribute_name = physical_name("selected-items", "tenant-a");
1284 let channel_map_name = physical_name("selected-by-tenant", "tenant-a");
1285 let mut context = Context::new(
1286 ContextInput {
1287 method: InvocationMethod::Rpc,
1288 flow: registered,
1289 metadata: ProtoContext::default(),
1290 attributes: vec![Kv {
1291 key: attribute_name.clone(),
1292 value: Some(value_mapper::encode_handler(&"value".to_string()).unwrap()),
1293 }],
1294 locals: Vec::new(),
1295 condition_results: None,
1296 channel_infos: HashMap::new(),
1297 loaded_channel_messages: HashMap::from([
1298 (
1299 "selected-commands".to_string(),
1300 ChannelValues {
1301 messages: vec![ProtoChannelMessage {
1302 channel_name: "selected-commands".to_string(),
1303 value: Some(
1304 value_mapper::encode_handler(&"first".to_string()).unwrap(),
1305 ),
1306 message_id: "message-1".to_string(),
1307 }],
1308 },
1309 ),
1310 (
1311 channel_map_name.clone(),
1312 ChannelValues {
1313 messages: vec![ProtoChannelMessage {
1314 channel_name: channel_map_name.clone(),
1315 value: Some(
1316 value_mapper::encode_handler(&"second".to_string()).unwrap(),
1317 ),
1318 message_id: "message-2".to_string(),
1319 }],
1320 },
1321 ),
1322 ]),
1323 loaded_attribute_map_instances: vec![attribute_name],
1324 loaded_channel_names: vec!["selected-commands".to_string()],
1325 loaded_channel_map_instances: vec![channel_map_name],
1326 },
1327 None,
1328 InvocationCancellation::new(),
1329 )
1330 .expect("create selective RPC Context");
1331
1332 assert_eq!(
1333 Some("value".to_string()),
1334 attributes.get(&context, "tenant-a").unwrap()
1335 );
1336 assert_eq!("first", queued.pending_messages(&context).unwrap()[0].value);
1337 assert_eq!(
1338 "message-2",
1339 by_tenant.pending_messages(&context, "tenant-a").unwrap()[0].message_id
1340 );
1341 assert_eq!(
1342 Some("first".to_string()),
1343 queued
1344 .find_pending_message(&context, "message-1")
1345 .unwrap()
1346 .map(|message| message.value)
1347 );
1348 assert!(attributes.get(&context, "other").is_err());
1349 assert!(by_tenant.pending_messages(&context, "other").is_err());
1350 assert!(queued.publish(&mut context, "later".to_string()).is_ok());
1351 assert_eq!(1, queued.pending_messages(&context).unwrap().len());
1352 }
1353}