1
1
//! Utilities for and tracking of internal versions which alter history in incompatible ways
2
2
//! so that we can use older code paths for workflows executed on older core versions.
3
3
4
- use std:: collections:: { BTreeSet , HashSet } ;
4
+ use itertools:: Either ;
5
+ use std:: {
6
+ collections:: { BTreeSet , HashSet } ,
7
+ iter,
8
+ } ;
5
9
use temporal_sdk_core_protos:: temporal:: api:: {
6
10
history:: v1:: WorkflowTaskCompletedEventAttributes , sdk:: v1:: WorkflowTaskCompletedMetadata ,
7
11
workflowservice:: v1:: get_system_info_response,
@@ -15,7 +19,7 @@ use temporal_sdk_core_protos::temporal::api::{
15
19
/// that removing older variants does not create any change in existing values. Removed flag
16
20
/// variants must be reserved forever (a-la protobuf), and should be called out in a comment.
17
21
#[ repr( u32 ) ]
18
- #[ derive( PartialEq , Eq , PartialOrd , Ord , Hash , Copy , Clone , Debug ) ]
22
+ #[ derive( PartialEq , Eq , PartialOrd , Ord , Hash , Copy , Clone , Debug , enum_iterator :: Sequence ) ]
19
23
pub ( crate ) enum CoreInternalFlags {
20
24
/// In this flag additional checks were added to a number of state machines to ensure that
21
25
/// the ID and type of activities, local activities, and child workflows match during replay.
@@ -28,83 +32,125 @@ pub(crate) enum CoreInternalFlags {
28
32
}
29
33
30
34
#[ derive( Debug , Clone , PartialEq , Eq ) ]
31
- pub ( crate ) struct InternalFlags {
32
- enabled : bool ,
33
- core : BTreeSet < CoreInternalFlags > ,
34
- lang : BTreeSet < u32 > ,
35
- core_since_last_complete : HashSet < CoreInternalFlags > ,
36
- lang_since_last_complete : HashSet < u32 > ,
35
+ pub ( crate ) enum InternalFlags {
36
+ Enabled {
37
+ core : BTreeSet < CoreInternalFlags > ,
38
+ lang : BTreeSet < u32 > ,
39
+ core_since_last_complete : HashSet < CoreInternalFlags > ,
40
+ lang_since_last_complete : HashSet < u32 > ,
41
+ } ,
42
+ Disabled ,
37
43
}
38
44
39
45
impl InternalFlags {
40
46
pub fn new ( server_capabilities : & get_system_info_response:: Capabilities ) -> Self {
41
- Self {
42
- enabled : server_capabilities. sdk_metadata ,
43
- core : Default :: default ( ) ,
44
- lang : Default :: default ( ) ,
45
- core_since_last_complete : Default :: default ( ) ,
46
- lang_since_last_complete : Default :: default ( ) ,
47
+ match server_capabilities. sdk_metadata {
48
+ true => Self :: Enabled {
49
+ core : Default :: default ( ) ,
50
+ lang : Default :: default ( ) ,
51
+ core_since_last_complete : Default :: default ( ) ,
52
+ lang_since_last_complete : Default :: default ( ) ,
53
+ } ,
54
+ false => Self :: Disabled ,
47
55
}
48
56
}
49
57
50
58
pub fn add_from_complete ( & mut self , e : & WorkflowTaskCompletedEventAttributes ) {
51
- if !self . enabled {
52
- return ;
53
- }
54
-
55
- if let Some ( metadata) = e. sdk_metadata . as_ref ( ) {
56
- self . core . extend (
57
- metadata
58
- . core_used_flags
59
- . iter ( )
60
- . map ( |u| CoreInternalFlags :: from_u32 ( * u) ) ,
61
- ) ;
62
- self . lang . extend ( metadata. lang_used_flags . iter ( ) ) ;
59
+ if let Self :: Enabled { core, lang, .. } = self {
60
+ if let Some ( metadata) = e. sdk_metadata . as_ref ( ) {
61
+ core. extend (
62
+ metadata
63
+ . core_used_flags
64
+ . iter ( )
65
+ . map ( |u| CoreInternalFlags :: from_u32 ( * u) ) ,
66
+ ) ;
67
+ lang. extend ( metadata. lang_used_flags . iter ( ) ) ;
68
+ }
63
69
}
64
70
}
65
71
66
72
pub fn add_lang_used ( & mut self , flags : impl IntoIterator < Item = u32 > ) {
67
- if !self . enabled {
68
- return ;
73
+ if let Self :: Enabled {
74
+ lang_since_last_complete,
75
+ ..
76
+ } = self
77
+ {
78
+ lang_since_last_complete. extend ( flags. into_iter ( ) ) ;
69
79
}
70
-
71
- self . lang_since_last_complete . extend ( flags. into_iter ( ) ) ;
72
80
}
73
81
74
82
/// Returns true if this flag may currently be used. If `should_record` is true, always returns
75
83
/// true and records the flag as being used, for taking later via
76
84
/// [Self::gather_for_wft_complete].
77
85
pub fn try_use ( & mut self , core_patch : CoreInternalFlags , should_record : bool ) -> bool {
78
- if !self . enabled {
86
+ match self {
87
+ Self :: Enabled {
88
+ core,
89
+ core_since_last_complete,
90
+ ..
91
+ } => {
92
+ if should_record {
93
+ core_since_last_complete. insert ( core_patch) ;
94
+ true
95
+ } else {
96
+ core. contains ( & core_patch)
97
+ }
98
+ }
79
99
// If the server does not support the metadata field, we must assume we can never use
80
100
// any internal flags since they can't be recorded for future use
81
- return false ;
101
+ Self :: Disabled => false ,
82
102
}
103
+ }
83
104
84
- if should_record {
85
- self . core_since_last_complete . insert ( core_patch) ;
86
- true
87
- } else {
88
- self . core . contains ( & core_patch)
105
+ /// Writes all known core flags to the set which should be recorded in the current WFT if not
106
+ /// already known. Must only be called if not replaying.
107
+ pub fn write_all_known ( & mut self ) {
108
+ if let Self :: Enabled {
109
+ core_since_last_complete,
110
+ ..
111
+ } = self
112
+ {
113
+ core_since_last_complete. extend ( CoreInternalFlags :: all_except_too_high ( ) ) ;
89
114
}
90
115
}
91
116
92
117
/// Wipes the recorded flags used during the current WFT and returns a partially filled
93
118
/// sdk metadata message that can be combined with any existing data before sending the WFT
94
119
/// complete
95
120
pub fn gather_for_wft_complete ( & mut self ) -> WorkflowTaskCompletedMetadata {
96
- WorkflowTaskCompletedMetadata {
97
- core_used_flags : self
98
- . core_since_last_complete
99
- . drain ( )
100
- . map ( |p| p as u32 )
101
- . collect ( ) ,
102
- lang_used_flags : self . lang_since_last_complete . drain ( ) . collect ( ) ,
121
+ match self {
122
+ Self :: Enabled {
123
+ core_since_last_complete,
124
+ lang_since_last_complete,
125
+ core,
126
+ lang,
127
+ } => {
128
+ let core_newly_used: Vec < _ > = core_since_last_complete
129
+ . iter ( )
130
+ . filter ( |f| !core. contains ( f) )
131
+ . map ( |p| * p as u32 )
132
+ . collect ( ) ;
133
+ let lang_newly_used: Vec < _ > = lang_since_last_complete
134
+ . iter ( )
135
+ . filter ( |f| !lang. contains ( f) )
136
+ . copied ( )
137
+ . collect ( ) ;
138
+ core. extend ( core_since_last_complete. iter ( ) ) ;
139
+ lang. extend ( lang_since_last_complete. iter ( ) ) ;
140
+ WorkflowTaskCompletedMetadata {
141
+ core_used_flags : core_newly_used,
142
+ lang_used_flags : lang_newly_used,
143
+ }
144
+ }
145
+ Self :: Disabled => WorkflowTaskCompletedMetadata :: default ( ) ,
103
146
}
104
147
}
105
148
106
- pub fn all_lang ( & self ) -> & BTreeSet < u32 > {
107
- & self . lang
149
+ pub fn all_lang ( & self ) -> impl Iterator < Item = u32 > + ' _ {
150
+ match self {
151
+ Self :: Enabled { lang, .. } => Either :: Left ( lang. iter ( ) . copied ( ) ) ,
152
+ Self :: Disabled => Either :: Right ( iter:: empty ( ) ) ,
153
+ }
108
154
}
109
155
}
110
156
@@ -116,6 +162,11 @@ impl CoreInternalFlags {
116
162
_ => Self :: TooHigh ,
117
163
}
118
164
}
165
+
166
+ pub fn all_except_too_high ( ) -> impl Iterator < Item = CoreInternalFlags > {
167
+ enum_iterator:: all :: < CoreInternalFlags > ( )
168
+ . filter ( |f| !matches ! ( f, CoreInternalFlags :: TooHigh ) )
169
+ }
119
170
}
120
171
121
172
#[ cfg( test) ]
@@ -138,4 +189,39 @@ mod tests {
138
189
assert_matches ! ( gathered. core_used_flags. as_slice( ) , & [ ] ) ;
139
190
assert_matches ! ( gathered. lang_used_flags. as_slice( ) , & [ ] ) ;
140
191
}
192
+
193
+ #[ test]
194
+ fn all_have_u32_from_impl ( ) {
195
+ let all_known = CoreInternalFlags :: all_except_too_high ( ) ;
196
+ for flag in all_known {
197
+ let as_u32 = flag as u32 ;
198
+ assert_eq ! ( CoreInternalFlags :: from_u32( as_u32) , flag) ;
199
+ }
200
+ }
201
+
202
+ #[ test]
203
+ fn only_writes_new_flags ( ) {
204
+ let mut f = InternalFlags :: new ( & Capabilities {
205
+ sdk_metadata : true ,
206
+ ..Default :: default ( )
207
+ } ) ;
208
+ f. add_lang_used ( [ 1 ] ) ;
209
+ f. try_use ( CoreInternalFlags :: IdAndTypeDeterminismChecks , true ) ;
210
+ let gathered = f. gather_for_wft_complete ( ) ;
211
+ assert_matches ! ( gathered. core_used_flags. as_slice( ) , & [ 1 ] ) ;
212
+ assert_matches ! ( gathered. lang_used_flags. as_slice( ) , & [ 1 ] ) ;
213
+
214
+ f. add_from_complete ( & WorkflowTaskCompletedEventAttributes {
215
+ sdk_metadata : Some ( WorkflowTaskCompletedMetadata {
216
+ core_used_flags : vec ! [ 2 ] ,
217
+ lang_used_flags : vec ! [ 2 ] ,
218
+ } ) ,
219
+ ..Default :: default ( )
220
+ } ) ;
221
+ f. add_lang_used ( [ 2 ] ) ;
222
+ f. try_use ( CoreInternalFlags :: UpsertSearchAttributeOnPatch , true ) ;
223
+ let gathered = f. gather_for_wft_complete ( ) ;
224
+ assert_matches ! ( gathered. core_used_flags. as_slice( ) , & [ ] ) ;
225
+ assert_matches ! ( gathered. lang_used_flags. as_slice( ) , & [ ] ) ;
226
+ }
141
227
}
0 commit comments