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 = 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}