reifydb_sub_flow/operator/stateful/
single.rs1use reifydb_codec::{
4 encoded::{row::EncodedRow, shape::RowShape},
5 key::encoded::EncodedKey,
6};
7use reifydb_value::Result;
8
9use super::utils;
10use crate::{operator::stateful::raw::RawStatefulOperator, transaction::FlowTransaction};
11
12pub trait SingleStateful: RawStatefulOperator {
13 fn layout(&self) -> RowShape;
14
15 fn key(&self) -> EncodedKey {
16 utils::empty_key()
17 }
18
19 fn create_state(&self) -> EncodedRow {
20 let layout = self.layout();
21 layout.allocate()
22 }
23
24 fn load_state(&self, txn: &mut FlowTransaction) -> Result<EncodedRow> {
25 let key = self.key();
26 utils::load_or_create_row(self.id(), txn, &key, &self.layout())
27 }
28
29 fn save_state(&self, txn: &mut FlowTransaction, row: EncodedRow) -> Result<()> {
30 let key = self.key();
31 utils::save_row(self.id(), txn, &key, row)
32 }
33
34 fn update_state<F>(&self, txn: &mut FlowTransaction, f: F) -> Result<EncodedRow>
35 where
36 F: FnOnce(&RowShape, &mut EncodedRow) -> Result<()>,
37 {
38 let shape = self.layout();
39 let mut row = self.load_state(txn)?;
40 f(&shape, &mut row)?;
41 self.save_state(txn, row.clone())?;
42 Ok(row)
43 }
44
45 fn clear_state(&self, txn: &mut FlowTransaction) -> Result<()> {
46 let key = self.key();
47 utils::state_remove(self.id(), txn, &key)
48 }
49}
50
51#[cfg(test)]
52pub mod tests {
53 use reifydb_catalog::catalog::Catalog;
54 use reifydb_core::{common::CommitVersion, interface::catalog::flow::FlowNodeId};
55 use reifydb_runtime::context::clock::{Clock, MockClock};
56 use reifydb_transaction::interceptor::interceptors::Interceptors;
57
58 use super::*;
59 use crate::{operator::stateful::test_utils::test::*, transaction::FlowTransaction};
60
61 impl SingleStateful for TestOperator {
63 fn layout(&self) -> RowShape {
64 self.layout.clone()
65 }
66 }
67
68 #[test]
69 fn testault_key() {
70 let operator = TestOperator::simple(FlowNodeId(1));
71 let key = operator.key();
72
73 assert_eq!(key.len(), 0);
75 }
76
77 #[test]
78 fn test_create_state() {
79 let operator = TestOperator::simple(FlowNodeId(1));
80 let state = operator.create_state();
81
82 assert!(state.len() > 0);
84 }
85
86 #[test]
87 fn test_load_save_state() {
88 let mut txn = create_test_transaction();
89 let mut txn = FlowTransaction::deferred(
90 &mut txn,
91 CommitVersion(1),
92 Catalog::testing(),
93 Interceptors::new(),
94 Clock::Mock(MockClock::from_millis(1000)),
95 );
96 let operator = TestOperator::simple(FlowNodeId(1));
97
98 let state1 = operator.load_state(&mut txn).unwrap();
100
101 let mut modified = state1.clone();
103 let layout = operator.layout();
104 layout.set_i64(&mut modified, 0, 0x33);
105 operator.save_state(&mut txn, modified.clone()).unwrap();
106
107 let state2 = operator.load_state(&mut txn).unwrap();
109 assert_eq!(layout.get_i64(&state2, 0), 0x33);
110 }
111
112 #[test]
113 fn test_update_state() {
114 let mut txn = create_test_transaction();
115 let mut txn = FlowTransaction::deferred(
116 &mut txn,
117 CommitVersion(1),
118 Catalog::testing(),
119 Interceptors::new(),
120 Clock::Mock(MockClock::from_millis(1000)),
121 );
122 let operator = TestOperator::simple(FlowNodeId(1));
123
124 let result = operator
126 .update_state(&mut txn, |shape, row| {
127 shape.set_i64(row, 0, 0x77);
128 Ok(())
129 })
130 .unwrap();
131
132 let layout = operator.layout();
133 assert_eq!(layout.get_i64(&result, 0), 0x77);
134
135 let loaded = operator.load_state(&mut txn).unwrap();
137 assert_eq!(layout.get_i64(&loaded, 0), 0x77);
138 }
139
140 #[test]
141 fn test_clear_state() {
142 let mut txn = create_test_transaction();
143 let mut txn = FlowTransaction::deferred(
144 &mut txn,
145 CommitVersion(1),
146 Catalog::testing(),
147 Interceptors::new(),
148 Clock::Mock(MockClock::from_millis(1000)),
149 );
150 let operator = TestOperator::simple(FlowNodeId(1));
151
152 operator.update_state(&mut txn, |shape, row| {
154 shape.set_i64(row, 0, 0x99);
155 Ok(())
156 })
157 .unwrap();
158
159 operator.clear_state(&mut txn).unwrap();
161
162 let new_state = operator.load_state(&mut txn).unwrap();
164 let layout = operator.layout();
165 assert_eq!(layout.get_i64(&new_state, 0), 0); }
167
168 #[test]
169 fn test_multiple_operators_isolated() {
170 let mut txn = create_test_transaction();
171 let mut txn = FlowTransaction::deferred(
172 &mut txn,
173 CommitVersion(1),
174 Catalog::testing(),
175 Interceptors::new(),
176 Clock::Mock(MockClock::from_millis(1000)),
177 );
178 let operator1 = TestOperator::simple(FlowNodeId(1));
179 let operator2 = TestOperator::simple(FlowNodeId(2));
180
181 operator1
183 .update_state(&mut txn, |shape, row| {
184 shape.set_i64(row, 0, 0x11);
185 Ok(())
186 })
187 .unwrap();
188
189 operator2
190 .update_state(&mut txn, |shape, row| {
191 shape.set_i64(row, 0, 0x22);
192 Ok(())
193 })
194 .unwrap();
195
196 let state1 = operator1.load_state(&mut txn).unwrap();
198 let state2 = operator2.load_state(&mut txn).unwrap();
199
200 let layout1 = operator1.layout();
201 let layout2 = operator2.layout();
202 assert_eq!(layout1.get_i64(&state1, 0), 0x11);
203 assert_eq!(layout2.get_i64(&state2, 0), 0x22);
204 }
205
206 #[test]
207 fn test_counter_simulation() {
208 let mut txn = create_test_transaction();
209 let mut txn = FlowTransaction::deferred(
210 &mut txn,
211 CommitVersion(1),
212 Catalog::testing(),
213 Interceptors::new(),
214 Clock::Mock(MockClock::from_millis(1000)),
215 );
216 let operator = TestOperator::new(FlowNodeId(1));
217
218 for i in 1..=5 {
220 operator.update_state(&mut txn, |shape, row| {
221 let current = shape.get_i64(row, 0);
223 shape.set_i64(row, 0, current + 1);
224 Ok(())
225 })
226 .unwrap();
227
228 let state = operator.load_state(&mut txn).unwrap();
229 let layout = operator.layout();
230 assert_eq!(layout.get_i64(&state, 0), i);
231 }
232 }
233}