| 130 | |
| 131 | impl OpLog for OriginOpLog { |
| 132 | fn append(&self, op: &ArrayOp) -> ArrayResult<()> { |
| 133 | let key = op_key(&op.header.array, op.header.hlc).ok_or_else(|| ArrayError::InvalidOp { |
| 134 | detail: format!("array name too long (>255 bytes): '{}'", op.header.array), |
| 135 | })?; |
| 136 | let encoded = op_codec::encode_op(op).map_err(|e| ArrayError::InvalidOp { |
| 137 | detail: format!("op_log append encode: {e}"), |
| 138 | })?; |
| 139 | |
| 140 | let txn = self.db.begin_write().map_err(|e| ArrayError::InvalidOp { |
| 141 | detail: format!("op_log append begin_write: {e}"), |
| 142 | })?; |
| 143 | { |
| 144 | let mut table = txn |
| 145 | .open_table(ARRAY_OP_LOG) |
| 146 | .map_err(|e| ArrayError::InvalidOp { |
| 147 | detail: format!("op_log append open_table: {e}"), |
| 148 | })?; |
| 149 | // Idempotent: insert only if the key is absent. |
| 150 | if table |
| 151 | .get(key.as_slice()) |
| 152 | .map_err(|e| ArrayError::InvalidOp { |
| 153 | detail: format!("op_log append get: {e}"), |
| 154 | })? |
| 155 | .is_none() |
| 156 | { |
| 157 | table |
| 158 | .insert(key.as_slice(), encoded.as_slice()) |
| 159 | .map_err(|e| ArrayError::InvalidOp { |
| 160 | detail: format!("op_log append insert: {e}"), |
| 161 | })?; |
| 162 | } |
| 163 | } |
| 164 | txn.commit().map_err(|e| ArrayError::InvalidOp { |
| 165 | detail: format!("op_log append commit: {e}"), |
| 166 | })?; |
| 167 | Ok(()) |
| 168 | } |
| 169 | |
| 170 | fn scan_from<'a>(&'a self, from: Hlc) -> ArrayResult<OpIter<'a>> { |
| 171 | let txn = self.db.begin_read().map_err(|e| ArrayError::InvalidOp { |