Skip to content

Commit 496c9f6

Browse files
Add option to disable non deterministic checks on payloads (#48)
1 parent bf8a8a1 commit 496c9f6

File tree

7 files changed

+275
-31
lines changed

7 files changed

+275
-31
lines changed

src/lib.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,9 +200,19 @@ pub enum ImplicitCancellationOption {
200200
},
201201
}
202202

203+
#[derive(Debug, Default)]
204+
pub enum NonDeterministicChecksOption {
205+
/// This will disable checking payloads (state values, rpc request, complete awakeable value),
206+
/// but will still check all the other commands parameters.
207+
PayloadChecksDisabled,
208+
#[default]
209+
Enabled,
210+
}
211+
203212
#[derive(Debug)]
204213
pub struct VMOptions {
205214
pub implicit_cancellation: ImplicitCancellationOption,
215+
pub non_determinism_checks: NonDeterministicChecksOption,
206216
}
207217

208218
impl Default for VMOptions {
@@ -212,6 +222,7 @@ impl Default for VMOptions {
212222
cancel_children_calls: true,
213223
cancel_children_one_way_calls: false,
214224
},
225+
non_determinism_checks: NonDeterministicChecksOption::Enabled,
215226
}
216227
}
217228
}

src/service_protocol/messages.rs

Lines changed: 136 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub trait NamedCommandMessage {
1717
}
1818

1919
pub trait CommandMessageHeaderEq {
20-
fn header_eq(&self, other: &Self) -> bool;
20+
fn header_eq(&self, other: &Self, ignore_payload_equality: bool) -> bool;
2121
}
2222

2323
pub trait CommandMessageHeaderDiff {
@@ -36,6 +36,10 @@ macro_rules! impl_message_traits {
3636
($name:ident: command) => {
3737
impl_message_traits!($name: message);
3838
impl_message_traits!($name: named_command);
39+
};
40+
($name:ident: command eq) => {
41+
impl_message_traits!($name: message);
42+
impl_message_traits!($name: named_command);
3943
impl_message_traits!($name: command_header_eq);
4044
};
4145
($name:ident: message) => {
@@ -54,7 +58,7 @@ macro_rules! impl_message_traits {
5458
};
5559
($name:ident: command_header_eq) => {
5660
impl CommandMessageHeaderEq for paste! { [<$name Message>] } {
57-
fn header_eq(&self, other: &Self) -> bool {
61+
fn header_eq(&self, other: &Self, _: bool) -> bool {
5862
self.eq(other)
5963
}
6064
}
@@ -69,53 +73,140 @@ impl_message_traits!(End: core);
6973
impl_message_traits!(ProposeRunCompletion: core);
7074

7175
// -- Entries
72-
impl_message_traits!(InputCommand: message);
73-
impl_message_traits!(InputCommand: named_command);
76+
impl_message_traits!(InputCommand: command);
7477
impl CommandMessageHeaderEq for InputCommandMessage {
75-
fn header_eq(&self, _: &Self) -> bool {
78+
fn header_eq(&self, _: &Self, _: bool) -> bool {
7679
true
7780
}
7881
}
7982

8083
impl_message_traits!(OutputCommand: command);
81-
impl_message_traits!(GetLazyStateCommand: command);
84+
impl CommandMessageHeaderEq for OutputCommandMessage {
85+
fn header_eq(&self, other: &Self, ignore_payload_checks: bool) -> bool {
86+
if ignore_payload_checks {
87+
self.name == other.name
88+
&& match (&self.result, &other.result) {
89+
(
90+
Some(output_command_message::Result::Value(_)),
91+
Some(output_command_message::Result::Value(_)),
92+
) => true,
93+
(x, y) => x.eq(y),
94+
}
95+
} else {
96+
self.eq(other)
97+
}
98+
}
99+
}
100+
101+
impl_message_traits!(GetLazyStateCommand: command eq);
82102
impl_message_traits!(GetLazyStateCompletionNotification: notification);
103+
83104
impl_message_traits!(SetStateCommand: command);
84-
impl_message_traits!(ClearStateCommand: command);
85-
impl_message_traits!(ClearAllStateCommand: command);
86-
impl_message_traits!(GetLazyStateKeysCommand: command);
105+
impl CommandMessageHeaderEq for SetStateCommandMessage {
106+
fn header_eq(&self, other: &Self, ignore_payload_checks: bool) -> bool {
107+
if ignore_payload_checks {
108+
self.name == other.name
109+
&& self.key == other.key
110+
&& match (&self.value, &other.value) {
111+
(Some(_), Some(_)) => true,
112+
(x, y) => x.eq(y),
113+
}
114+
} else {
115+
self.eq(other)
116+
}
117+
}
118+
}
119+
120+
impl_message_traits!(ClearStateCommand: command eq);
121+
122+
impl_message_traits!(ClearAllStateCommand: command eq);
123+
124+
impl_message_traits!(GetLazyStateKeysCommand: command eq);
87125
impl_message_traits!(GetLazyStateKeysCompletionNotification: notification);
126+
88127
impl_message_traits!(GetEagerStateCommand: command);
89-
impl_message_traits!(GetEagerStateKeysCommand: command);
90-
impl_message_traits!(GetPromiseCommand: command);
128+
impl CommandMessageHeaderEq for GetEagerStateCommandMessage {
129+
fn header_eq(&self, other: &Self, ignore_payload_checks: bool) -> bool {
130+
if ignore_payload_checks {
131+
self.name == other.name
132+
&& self.key == other.key
133+
&& match (&self.result, &other.result) {
134+
(
135+
Some(get_eager_state_command_message::Result::Value(_)),
136+
Some(get_eager_state_command_message::Result::Value(_)),
137+
) => true,
138+
(x, y) => x.eq(y),
139+
}
140+
} else {
141+
self.eq(other)
142+
}
143+
}
144+
}
145+
146+
impl_message_traits!(GetEagerStateKeysCommand: command eq);
147+
148+
impl_message_traits!(GetPromiseCommand: command eq);
91149
impl_message_traits!(GetPromiseCompletionNotification: notification);
92-
impl_message_traits!(PeekPromiseCommand: command);
150+
151+
impl_message_traits!(PeekPromiseCommand: command eq);
93152
impl_message_traits!(PeekPromiseCompletionNotification: notification);
153+
94154
impl_message_traits!(CompletePromiseCommand: command);
155+
impl CommandMessageHeaderEq for CompletePromiseCommandMessage {
156+
fn header_eq(&self, other: &Self, ignore_payload_checks: bool) -> bool {
157+
if ignore_payload_checks {
158+
self.name == other.name
159+
&& self.key == other.key
160+
&& self.result_completion_id == other.result_completion_id
161+
&& match (&self.completion, &other.completion) {
162+
(
163+
Some(complete_promise_command_message::Completion::CompletionValue(_)),
164+
Some(complete_promise_command_message::Completion::CompletionValue(_)),
165+
) => true,
166+
(x, y) => x.eq(y),
167+
}
168+
} else {
169+
self.eq(other)
170+
}
171+
}
172+
}
95173
impl_message_traits!(CompletePromiseCompletionNotification: notification);
96174

97-
impl_message_traits!(SleepCommand: message);
98-
impl_message_traits!(SleepCommand: named_command);
175+
impl_message_traits!(SleepCommand: command);
99176
impl CommandMessageHeaderEq for SleepCommandMessage {
100-
fn header_eq(&self, other: &Self) -> bool {
177+
fn header_eq(&self, other: &Self, _: bool) -> bool {
101178
self.name == other.name
102179
}
103180
}
104-
105181
impl_message_traits!(SleepCompletionNotification: notification);
182+
106183
impl_message_traits!(CallCommand: command);
184+
impl CommandMessageHeaderEq for CallCommandMessage {
185+
fn header_eq(&self, other: &Self, ignore_payload_checks: bool) -> bool {
186+
self.service_name == other.service_name
187+
&& self.handler_name == other.handler_name
188+
&& (ignore_payload_checks || (self.parameter == other.parameter))
189+
&& self.headers == other.headers
190+
&& self.key == other.key
191+
&& self.idempotency_key == other.idempotency_key
192+
&& self.invocation_id_notification_idx == other.invocation_id_notification_idx
193+
&& self.result_completion_id == other.result_completion_id
194+
&& self.name == other.name
195+
}
196+
}
107197
impl_message_traits!(CallInvocationIdCompletionNotification: notification);
108198
impl_message_traits!(CallCompletionNotification: notification);
109199

110-
impl_message_traits!(OneWayCallCommand: message);
111-
impl_message_traits!(OneWayCallCommand: named_command);
200+
impl_message_traits!(OneWayCallCommand: command);
112201
impl CommandMessageHeaderEq for OneWayCallCommandMessage {
113-
fn header_eq(&self, other: &Self) -> bool {
202+
fn header_eq(&self, other: &Self, ignore_payload_checks: bool) -> bool {
114203
self.service_name == other.service_name
115204
&& self.handler_name == other.handler_name
116-
&& self.key == other.key
205+
&& (ignore_payload_checks || (self.parameter == other.parameter))
117206
&& self.headers == other.headers
118-
&& self.parameter == other.parameter
207+
&& self.key == other.key
208+
&& self.idempotency_key == other.idempotency_key
209+
&& self.invocation_id_notification_idx == other.invocation_id_notification_idx
119210
&& self.name == other.name
120211
}
121212
}
@@ -128,13 +219,34 @@ impl NamedCommandMessage for SendSignalCommandMessage {
128219
}
129220
impl_message_traits!(SendSignalCommand: command_header_eq);
130221

131-
impl_message_traits!(RunCommand: command);
222+
impl_message_traits!(RunCommand: command eq);
132223
impl_message_traits!(RunCompletionNotification: notification);
133-
impl_message_traits!(AttachInvocationCommand: command);
224+
225+
impl_message_traits!(AttachInvocationCommand: command eq);
134226
impl_message_traits!(AttachInvocationCompletionNotification: notification);
135-
impl_message_traits!(GetInvocationOutputCommand: command);
227+
228+
impl_message_traits!(GetInvocationOutputCommand: command eq);
136229
impl_message_traits!(GetInvocationOutputCompletionNotification: notification);
230+
137231
impl_message_traits!(CompleteAwakeableCommand: command);
232+
impl CommandMessageHeaderEq for CompleteAwakeableCommandMessage {
233+
fn header_eq(&self, other: &Self, ignore_payload_checks: bool) -> bool {
234+
if ignore_payload_checks {
235+
self.name == other.name
236+
&& self.awakeable_id == other.awakeable_id
237+
&& match (&self.result, &other.result) {
238+
(
239+
Some(complete_awakeable_command_message::Result::Value(_)),
240+
Some(complete_awakeable_command_message::Result::Failure(_)),
241+
) => true,
242+
(x, y) => x.eq(y),
243+
}
244+
} else {
245+
self.eq(other)
246+
}
247+
}
248+
}
249+
138250
impl_message_traits!(SignalNotification: notification);
139251

140252
// --- Diffs and Formatting

src/tests/failures.rs

Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
use super::*;
22

33
use crate::error::CommandMetadata;
4+
use crate::service_protocol::messages::start_message::StateEntry;
45
use crate::service_protocol::messages::{
5-
CommandMessageHeaderDiff, ErrorMessage, GetLazyStateCommandMessage, OneWayCallCommandMessage,
6-
StartMessage,
6+
CallCommandMessage, CommandMessageHeaderDiff, EndMessage, ErrorMessage,
7+
GetEagerStateCommandMessage, GetLazyStateCommandMessage, OneWayCallCommandMessage,
8+
SetStateCommandMessage, StartMessage,
79
};
810
use crate::service_protocol::MessageType;
911
use std::fmt;
@@ -159,3 +161,104 @@ fn test_entry_mismatch_on_replay<
159161
);
160162
assert_eq!(output.next(), None);
161163
}
164+
165+
#[test]
166+
fn disable_non_deterministic_payload_checks() {
167+
let mut output = VMTestCase::with_vm_options(VMOptions {
168+
non_determinism_checks: NonDeterministicChecksOption::PayloadChecksDisabled,
169+
..VMOptions::default()
170+
})
171+
.input(StartMessage {
172+
id: Bytes::from_static(b"123"),
173+
debug_id: "123".to_string(),
174+
known_entries: 5,
175+
partial_state: true,
176+
state_map: vec![StateEntry {
177+
key: Bytes::from_static(b"STATE"),
178+
// NOTE: this is different payload than the one recorded in the entry!
179+
value: Bytes::from_static(b"456"),
180+
}],
181+
..Default::default()
182+
})
183+
.input(input_entry_message(b"my-data"))
184+
.input(OneWayCallCommandMessage {
185+
service_name: "greeter".to_owned(),
186+
handler_name: "greet".to_owned(),
187+
key: "my-key".to_owned(),
188+
parameter: Bytes::from_static(b"123"),
189+
invocation_id_notification_idx: 1,
190+
..Default::default()
191+
})
192+
.input(CallCommandMessage {
193+
service_name: "greeter".to_owned(),
194+
handler_name: "greet".to_owned(),
195+
key: "my-key".to_owned(),
196+
parameter: Bytes::from_static(b"123"),
197+
invocation_id_notification_idx: 2,
198+
result_completion_id: 3,
199+
..Default::default()
200+
})
201+
.input(SetStateCommandMessage {
202+
key: Bytes::from_static(b"my-key"),
203+
value: Some(messages::Value {
204+
content: Bytes::from_static(b"123"),
205+
}),
206+
..Default::default()
207+
})
208+
.input(GetEagerStateCommandMessage {
209+
key: Bytes::from_static(b"STATE"),
210+
result: Some(messages::get_eager_state_command_message::Result::Value(
211+
messages::Value {
212+
content: Bytes::from_static(b"123"),
213+
},
214+
)),
215+
..Default::default()
216+
})
217+
.run(|vm| {
218+
vm.sys_input().unwrap();
219+
220+
vm.sys_send(
221+
Target {
222+
service: "greeter".to_owned(),
223+
handler: "greet".to_owned(),
224+
key: Some("my-key".to_owned()),
225+
idempotency_key: None,
226+
headers: Vec::new(),
227+
},
228+
// NOTE: this is different payload!
229+
Bytes::from_static(b"456"),
230+
None,
231+
)
232+
.unwrap();
233+
234+
vm.sys_call(
235+
Target {
236+
service: "greeter".to_owned(),
237+
handler: "greet".to_owned(),
238+
key: Some("my-key".to_owned()),
239+
idempotency_key: None,
240+
headers: Vec::new(),
241+
},
242+
// NOTE: this is different payload!
243+
Bytes::from_static(b"456"),
244+
)
245+
.unwrap();
246+
247+
vm.sys_state_set(
248+
"my-key".to_owned(),
249+
// NOTE: this is different payload!
250+
Bytes::from_static(b"456"),
251+
)
252+
.unwrap();
253+
254+
vm.sys_state_get("STATE".to_owned()).unwrap();
255+
256+
vm.sys_end().unwrap();
257+
});
258+
259+
assert_eq!(
260+
output.next_decoded::<EndMessage>().unwrap(),
261+
EndMessage::default()
262+
);
263+
assert_eq!(output.next(), None);
264+
}

src/tests/implicit_cancellation.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ fn call_then_then_cancel_disabling_children_cancellation() {
192192
cancel_children_calls: false,
193193
cancel_children_one_way_calls: true,
194194
},
195+
..VMOptions::default()
195196
})
196197
.input(start_message(1))
197198
.input(input_entry_message(b"my-data"))
@@ -240,6 +241,7 @@ fn call_then_then_cancel_disabling_children_cancellation() {
240241
fn disabled_implicit_cancellation() {
241242
let mut output = VMTestCase::with_vm_options(VMOptions {
242243
implicit_cancellation: ImplicitCancellationOption::Disabled,
244+
..VMOptions::default()
243245
})
244246
.input(start_message(1))
245247
.input(input_entry_message(b"my-data"))

src/vm/context.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,7 @@ pub(crate) struct Context {
438438
pub(crate) input_is_closed: bool,
439439
pub(crate) output: Output,
440440
pub(crate) eager_state: EagerState,
441+
pub(crate) non_deterministic_checks_ignore_payload_equality: bool,
441442
}
442443

443444
impl Context {

0 commit comments

Comments
 (0)