little_durable_objects/
state_log.rs1use 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}