Skip to main content

little_durable_objects/
state_log.rs

1use anyhow::{Context, Result, ensure};
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4
5pub const MAX_ACTOR_STATE_BYTES: usize = 16 * 1024 * 1024;
6
7#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
8#[serde(rename_all = "camelCase")]
9pub struct StateSnapshot {
10    pub state_version: u64,
11    pub owner_epoch: u64,
12    pub request_id: String,
13    pub state: Value,
14    pub result: Value,
15}
16
17impl StateSnapshot {
18    pub fn new(
19        state_version: u64,
20        owner_epoch: u64,
21        request_id: String,
22        state: Value,
23        result: Value,
24    ) -> Result<Self> {
25        let snapshot = Self {
26            state_version,
27            owner_epoch,
28            request_id,
29            state,
30            result,
31        };
32        snapshot.validate()?;
33        Ok(snapshot)
34    }
35
36    pub fn decode(bytes: &[u8]) -> Result<Self> {
37        let snapshot: Self =
38            serde_json::from_slice(bytes).context("decode actor state snapshot")?;
39        snapshot.validate()?;
40        Ok(snapshot)
41    }
42
43    pub fn encode(&self) -> Result<Vec<u8>> {
44        self.validate()?;
45        Ok(serde_json::to_vec(self)?)
46    }
47
48    fn validate(&self) -> Result<()> {
49        ensure!(
50            self.state_version > 0,
51            "actor state version must be positive"
52        );
53        ensure!(self.owner_epoch > 0, "owner epoch must be positive");
54        ensure!(
55            !self.request_id.is_empty() && self.request_id.len() <= 255,
56            "actor state request ID is invalid"
57        );
58        ensure!(self.state.is_object(), "actor state must be a JSON object");
59        ensure!(
60            serde_json::to_vec(&self.state)?.len() <= MAX_ACTOR_STATE_BYTES,
61            "actor state exceeds the {MAX_ACTOR_STATE_BYTES}-byte limit"
62        );
63        Ok(())
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70    use serde_json::json;
71
72    #[test]
73    fn accepts_state_larger_than_the_previous_one_mib_limit() {
74        StateSnapshot::new(
75            1,
76            1,
77            "request-1".into(),
78            json!({"value": "x".repeat(2 * 1024 * 1024)}),
79            Value::Null,
80        )
81        .expect("state within the supported limit");
82    }
83
84    #[test]
85    fn rejects_oversized_state() {
86        let error = StateSnapshot::new(
87            1,
88            1,
89            "request-1".into(),
90            json!({"value": "x".repeat(MAX_ACTOR_STATE_BYTES)}),
91            Value::Null,
92        )
93        .expect_err("oversized state");
94        assert!(error.to_string().contains("actor state exceeds"));
95    }
96}