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 = 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    pub fn replay(&self, request_id: &str) -> Option<&Value> {
49        (self.request_id == request_id).then_some(&self.result)
50    }
51
52    fn validate(&self) -> Result<()> {
53        ensure!(
54            self.state_version > 0,
55            "actor state version must be positive"
56        );
57        ensure!(self.owner_epoch > 0, "owner epoch must be positive");
58        ensure!(
59            !self.request_id.is_empty() && self.request_id.len() <= 255,
60            "actor state request ID is invalid"
61        );
62        ensure!(self.state.is_object(), "actor state must be a JSON object");
63        ensure!(
64            serde_json::to_vec(&self.state)?.len() <= MAX_ACTOR_STATE_BYTES,
65            "actor state exceeds the {MAX_ACTOR_STATE_BYTES}-byte limit"
66        );
67        Ok(())
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74    use serde_json::json;
75
76    #[test]
77    fn rejects_oversized_state() {
78        let error = StateSnapshot::new(
79            1,
80            1,
81            "request-1".into(),
82            json!({"value": "x".repeat(MAX_ACTOR_STATE_BYTES)}),
83            Value::Null,
84        )
85        .expect_err("oversized state");
86        assert!(error.to_string().contains("actor state exceeds"));
87    }
88}