agentmux_srv/backend/storage/filestore/
ijson.rs1#![allow(dead_code)]
2use super::core::FileStore;
10use super::types::FileMeta;
11use crate::backend::storage::error::StoreError;
12
13impl FileStore {
14 const IJSON_NUM_COMMANDS: &'static str = "ijson:numcmds";
16 const IJSON_INC_BYTES: &'static str = "ijson:incbytes";
18
19 const IJSON_HIGH_COMMANDS: i64 = 100;
21 const IJSON_HIGH_RATIO: f64 = 3.0;
23 const IJSON_LOW_COMMANDS: i64 = 10;
25 const IJSON_LOW_RATIO: f64 = 1.0;
27
28 #[track_caller]
30 pub fn append_ijson(
31 &self,
32 zone_id: &str,
33 name: &str,
34 command: &serde_json::Value,
35 ) -> Result<(), StoreError> {
36 let file = self.stat(zone_id, name)?.ok_or(StoreError::NotFound)?;
37 if !file.opts.ijson {
38 return Err(StoreError::Other("file is not ijson".to_string()));
39 }
40
41 let cmd_bytes = serde_json::to_string(command)?;
42 let data = format!("{}\n", cmd_bytes);
43 self.append_data(zone_id, name, data.as_bytes())?;
44
45 let file = self.stat(zone_id, name)?.ok_or(StoreError::NotFound)?;
47 let mut meta = file.meta.clone();
48
49 let num_cmds = meta
50 .get(Self::IJSON_NUM_COMMANDS)
51 .and_then(|v| v.as_i64())
52 .unwrap_or(0)
53 + 1;
54 let inc_bytes = meta
55 .get(Self::IJSON_INC_BYTES)
56 .and_then(|v| v.as_i64())
57 .unwrap_or(0)
58 + data.len() as i64;
59
60 meta.insert(
61 Self::IJSON_NUM_COMMANDS.to_string(),
62 serde_json::json!(num_cmds),
63 );
64 meta.insert(
65 Self::IJSON_INC_BYTES.to_string(),
66 serde_json::json!(inc_bytes),
67 );
68 self.write_meta(zone_id, name, meta, false)?;
69
70 let file_size = file.size.max(1); let ratio = inc_bytes as f64 / file_size as f64;
73
74 let should_compact = num_cmds > Self::IJSON_HIGH_COMMANDS
75 || ratio >= Self::IJSON_HIGH_RATIO
76 || (num_cmds > Self::IJSON_LOW_COMMANDS && ratio >= Self::IJSON_LOW_RATIO);
77
78 if should_compact {
79 let _ = self.compact_ijson(zone_id, name);
80 }
81
82 Ok(())
83 }
84
85 #[track_caller]
88 pub fn compact_ijson(
89 &self,
90 zone_id: &str,
91 name: &str,
92 ) -> Result<(), StoreError> {
93 let data = self
95 .read_file(zone_id, name)?
96 .ok_or(StoreError::NotFound)?;
97
98 let content = String::from_utf8(data)
99 .map_err(|e| StoreError::Other(format!("invalid utf-8 in ijson file: {}", e)))?;
100
101 let compacted = crate::backend::ijson::compact_ijson(&content)
103 .map_err(|e| StoreError::Other(format!("ijson compact error: {}", e)))?;
104
105 let compacted_with_newline = format!("{}\n", compacted);
108 self.write_file(zone_id, name, compacted_with_newline.as_bytes())?;
109
110 let mut meta = FileMeta::new();
112 meta.insert(
113 Self::IJSON_NUM_COMMANDS.to_string(),
114 serde_json::json!(0),
115 );
116 meta.insert(
117 Self::IJSON_INC_BYTES.to_string(),
118 serde_json::json!(0),
119 );
120 self.write_meta(zone_id, name, meta, true)?;
121
122 Ok(())
123 }
124}