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