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