grovedb_costs/
lib.rs

1// MIT LICENSE
2//
3// Copyright (c) 2021 Dash Core Group
4//
5// Permission is hereby granted, free of charge, to any
6// person obtaining a copy of this software and associated
7// documentation files (the "Software"), to deal in the
8// Software without restriction, including without
9// limitation the rights to use, copy, modify, merge,
10// publish, distribute, sublicense, and/or sell copies of
11// the Software, and to permit persons to whom the Software
12// is furnished to do so, subject to the following
13// conditions:
14//
15// The above copyright notice and this permission notice
16// shall be included in all copies or substantial portions
17// of the Software.
18//
19// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
20// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
21// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
22// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
23// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
24// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
26// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27// DEALINGS IN THE SOFTWARE.
28
29#![deny(missing_docs)]
30
31//! Interface crate to unify how operations' costs are passed and retrieved.
32
33/// Cost Contexts
34pub mod context;
35/// Cost Errors
36pub mod error;
37/// Storage Costs
38pub mod storage_cost;
39
40use std::ops::{Add, AddAssign};
41
42pub use context::{CostContext, CostResult, CostsExt};
43use integer_encoding::VarInt;
44
45use crate::{
46    error::Error,
47    storage_cost::{
48        key_value_cost::KeyValueStorageCost, removal::StorageRemovedBytes, StorageCost,
49    },
50    StorageRemovedBytes::BasicStorageRemoval,
51};
52
53/// Child key length
54pub type ChildKeyLength = u32;
55
56/// Feature sum length
57pub type FeatureSumLength = u32;
58
59/// Child sum length
60pub type ChildSumLength = u32;
61
62/// Children sizes
63pub type ChildrenSizes = Option<(
64    Option<(ChildKeyLength, ChildSumLength)>,
65    Option<(ChildKeyLength, ChildSumLength)>,
66)>;
67
68/// Children sizes starting with a value
69pub type ChildrenSizesWithValue = Option<(
70    Vec<u8>,
71    Option<(ChildKeyLength, ChildSumLength)>,
72    Option<(ChildKeyLength, ChildSumLength)>,
73)>;
74
75/// The tree cost type
76pub enum TreeCostType {
77    /// This is for sum trees and count trees
78    TreeFeatureUsesVarIntCostAs8Bytes,
79    /// This is for count sum trees
80    TreeFeatureUsesTwoVarIntsCostAs16Bytes,
81    /// This is for big sum trees
82    TreeFeatureUses16Bytes,
83}
84
85impl TreeCostType {
86    fn cost_size(&self) -> u32 {
87        match self {
88            TreeCostType::TreeFeatureUsesVarIntCostAs8Bytes => 8,
89            TreeCostType::TreeFeatureUsesTwoVarIntsCostAs16Bytes => 16,
90            TreeCostType::TreeFeatureUses16Bytes => 16,
91        }
92    }
93}
94
95/// Children sizes starting with if we are in a sum tree
96pub type ChildrenSizesWithIsSumTree = Option<(
97    Option<(TreeCostType, FeatureSumLength)>,
98    Option<(ChildKeyLength, ChildSumLength)>,
99    Option<(ChildKeyLength, ChildSumLength)>,
100)>;
101
102/// Piece of data representing affected computer resources (approximately).
103#[derive(Debug, Default, Eq, PartialEq, Clone)]
104pub struct OperationCost {
105    /// How many storage_cost seeks were done.
106    pub seek_count: u32,
107    /// Storage cost of the operation.
108    pub storage_cost: StorageCost,
109    /// How many bytes were loaded from hard drive.
110    pub storage_loaded_bytes: u64,
111    /// How many times node hashing was done (for merkelized tree).
112    pub hash_node_calls: u32,
113}
114
115impl OperationCost {
116    /// Is Nothing
117    pub fn is_nothing(&self) -> bool {
118        self == &Self::default()
119    }
120
121    /// Helper function to build default `OperationCost` with different
122    /// `seek_count`.
123    pub fn with_seek_count(seek_count: u32) -> Self {
124        OperationCost {
125            seek_count,
126            ..Default::default()
127        }
128    }
129
130    /// Helper function to build default `OperationCost` with different
131    /// `storage_written_bytes`.
132    pub fn with_storage_written_bytes(storage_written_bytes: u32) -> Self {
133        OperationCost {
134            storage_cost: StorageCost {
135                added_bytes: storage_written_bytes,
136                ..Default::default()
137            },
138            ..Default::default()
139        }
140    }
141
142    /// Helper function to build default `OperationCost` with different
143    /// `storage_loaded_bytes`.
144    pub fn with_storage_loaded_bytes(storage_loaded_bytes: u64) -> Self {
145        OperationCost {
146            storage_loaded_bytes,
147            ..Default::default()
148        }
149    }
150
151    /// Helper function to build default `OperationCost` with different
152    /// `storage_freed_bytes`.
153    pub fn with_storage_freed_bytes(storage_freed_bytes: u32) -> Self {
154        OperationCost {
155            storage_cost: StorageCost {
156                removed_bytes: BasicStorageRemoval(storage_freed_bytes),
157                ..Default::default()
158            },
159            ..Default::default()
160        }
161    }
162
163    /// Helper function to build default `OperationCost` with different
164    /// `hash_node_calls`.
165    pub fn with_hash_node_calls(hash_node_calls: u32) -> Self {
166        OperationCost {
167            hash_node_calls,
168            ..Default::default()
169        }
170    }
171
172    /// worse_or_eq_than means worse for things that would cost resources
173    /// storage_freed_bytes is worse when it is lower instead
174    pub fn worse_or_eq_than(&self, other: &Self) -> bool {
175        self.seek_count >= other.seek_count
176            && self.storage_cost.worse_or_eq_than(&other.storage_cost)
177            && self.storage_loaded_bytes >= other.storage_loaded_bytes
178            && self.hash_node_calls >= other.hash_node_calls
179    }
180
181    /// add storage_cost costs for key and value storages
182    pub fn add_key_value_storage_costs(
183        &mut self,
184        key_len: u32,
185        value_len: u32,
186        children_sizes: ChildrenSizesWithIsSumTree,
187        storage_cost_info: Option<KeyValueStorageCost>,
188    ) -> Result<(), Error> {
189        let paid_key_len = key_len + key_len.required_space() as u32;
190
191        let doesnt_need_verification = storage_cost_info
192            .as_ref()
193            .map(|key_value_storage_cost| {
194                if !key_value_storage_cost.needs_value_verification {
195                    Some(
196                        key_value_storage_cost.value_storage_cost.added_bytes
197                            + key_value_storage_cost.value_storage_cost.replaced_bytes,
198                    )
199                } else {
200                    None
201                }
202            })
203            .unwrap_or(None);
204        let final_paid_value_len = if let Some(value_cost_len) = doesnt_need_verification {
205            value_cost_len
206        } else {
207            let mut paid_value_len = value_len;
208            // We need to remove the child sizes if they exist
209            if let Some((in_sum_tree, left_child, right_child)) = children_sizes {
210                paid_value_len -= 2; // for the child options
211
212                // We need to remove the costs of the children
213                if let Some((left_child_len, left_child_sum_len)) = left_child {
214                    paid_value_len -= left_child_len;
215                    paid_value_len -= left_child_sum_len;
216                }
217                if let Some((right_child_len, right_child_sum_len)) = right_child {
218                    paid_value_len -= right_child_len;
219                    paid_value_len -= right_child_sum_len;
220                }
221
222                let sum_tree_node_size = if let Some((tree_cost_type, sum_tree_len)) = in_sum_tree {
223                    let cost_size = tree_cost_type.cost_size();
224                    paid_value_len -= sum_tree_len;
225                    paid_value_len += cost_size;
226                    cost_size
227                } else {
228                    0
229                };
230
231                // This is the moment we need to add the required space (after removing
232                // children) but before adding the parent to child hook
233                paid_value_len += paid_value_len.required_space() as u32;
234
235                // Now we are the parent to child hook
236
237                // We need to add the cost of a parent
238                // key_len has a hash length already in it from the key prefix
239                // So we need to remove it and then add a hash length
240                // For the parent ref + 4 (2 for child sizes, 1 for key_len, 1 for sum option)
241
242                paid_value_len += key_len + 4 + sum_tree_node_size;
243            } else {
244                paid_value_len += paid_value_len.required_space() as u32;
245            }
246            paid_value_len
247        };
248
249        let (key_storage_cost, value_storage_costs) = match storage_cost_info {
250            None => (None, None),
251            Some(s) => {
252                s.key_storage_cost
253                    .verify_key_storage_cost(paid_key_len, s.new_node)?;
254                s.value_storage_cost.verify(final_paid_value_len)?;
255                (Some(s.key_storage_cost), Some(s.value_storage_cost))
256            }
257        };
258
259        self.add_storage_costs(paid_key_len, key_storage_cost);
260        self.add_storage_costs(final_paid_value_len, value_storage_costs);
261        Ok(())
262    }
263
264    /// add_storage_costs adds storage_cost costs for a key or a value
265    fn add_storage_costs(
266        &mut self,
267        len_with_required_space: u32,
268        storage_cost_info: Option<StorageCost>,
269    ) {
270        match storage_cost_info {
271            // There is no storage_cost cost info, just use value len
272            None => {
273                self.storage_cost += StorageCost {
274                    added_bytes: len_with_required_space,
275                    ..Default::default()
276                }
277            }
278            Some(storage_cost) => {
279                self.storage_cost += storage_cost;
280            }
281        }
282    }
283}
284
285impl Add for OperationCost {
286    type Output = Self;
287
288    fn add(self, rhs: Self) -> Self::Output {
289        OperationCost {
290            seek_count: self.seek_count + rhs.seek_count,
291            storage_cost: self.storage_cost + rhs.storage_cost,
292            storage_loaded_bytes: self.storage_loaded_bytes + rhs.storage_loaded_bytes,
293            hash_node_calls: self.hash_node_calls + rhs.hash_node_calls,
294        }
295    }
296}
297
298impl AddAssign for OperationCost {
299    fn add_assign(&mut self, rhs: Self) {
300        self.seek_count += rhs.seek_count;
301        self.storage_cost += rhs.storage_cost;
302        self.storage_loaded_bytes += rhs.storage_loaded_bytes;
303        self.hash_node_calls += rhs.hash_node_calls;
304    }
305}
306
307#[cfg(test)]
308mod tests {
309    use super::*;
310    use crate::context::{CostContext, CostResult, CostsExt};
311
312    #[test]
313    fn test_map() {
314        let initial = CostContext {
315            value: 75,
316            cost: OperationCost {
317                storage_loaded_bytes: 3,
318                ..Default::default()
319            },
320        };
321
322        let mapped = initial.map(|x| x + 25);
323        assert_eq!(
324            mapped,
325            CostContext {
326                value: 100,
327                cost: OperationCost {
328                    storage_loaded_bytes: 3,
329                    ..Default::default()
330                },
331            }
332        );
333    }
334
335    #[test]
336    fn test_flat_map() {
337        let initial = CostContext {
338            value: 75,
339            cost: OperationCost {
340                storage_loaded_bytes: 3,
341                ..Default::default()
342            },
343        };
344
345        let mapped = initial.flat_map(|x| CostContext {
346            value: x + 25,
347            cost: OperationCost {
348                storage_loaded_bytes: 7,
349                ..Default::default()
350            },
351        });
352        assert_eq!(
353            mapped,
354            CostContext {
355                value: 100,
356                cost: OperationCost {
357                    storage_loaded_bytes: 10,
358                    ..Default::default()
359                },
360            }
361        );
362    }
363
364    #[test]
365    fn test_map_ok() {
366        let initial: CostResult<usize, ()> = CostContext {
367            value: Ok(75),
368            cost: OperationCost {
369                storage_loaded_bytes: 3,
370                ..Default::default()
371            },
372        };
373
374        let mapped = initial.map_ok(|x| x + 25);
375        assert_eq!(
376            mapped,
377            CostContext {
378                value: Ok(100),
379                cost: OperationCost {
380                    storage_loaded_bytes: 3,
381                    ..Default::default()
382                },
383            }
384        );
385    }
386
387    #[test]
388    fn test_map_ok_err() {
389        let initial: CostResult<usize, ()> = CostContext {
390            value: Err(()),
391            cost: OperationCost {
392                storage_loaded_bytes: 3,
393                ..Default::default()
394            },
395        };
396
397        let mapped = initial.map_ok(|x| x + 25);
398        assert_eq!(
399            mapped,
400            CostContext {
401                value: Err(()),
402                cost: OperationCost {
403                    storage_loaded_bytes: 3,
404                    ..Default::default()
405                },
406            }
407        );
408    }
409
410    #[test]
411    fn test_flat_map_ok() {
412        let initial: CostResult<usize, ()> = CostContext {
413            value: Ok(75),
414            cost: OperationCost {
415                storage_loaded_bytes: 3,
416                ..Default::default()
417            },
418        };
419
420        let mapped = initial.flat_map_ok(|x| CostContext {
421            value: Ok(x + 25),
422            cost: OperationCost {
423                storage_loaded_bytes: 7,
424                ..Default::default()
425            },
426        });
427        assert_eq!(
428            mapped,
429            CostContext {
430                value: Ok(100),
431                cost: OperationCost {
432                    storage_loaded_bytes: 10,
433                    ..Default::default()
434                },
435            }
436        );
437    }
438
439    #[test]
440    fn test_flat_map_err_first() {
441        let initial: CostResult<usize, ()> = CostContext {
442            value: Err(()),
443            cost: OperationCost {
444                storage_loaded_bytes: 3,
445                ..Default::default()
446            },
447        };
448        let mut executed = false;
449        let mapped = initial.flat_map_ok(|x| {
450            executed = true;
451            CostContext {
452                value: Ok(x + 25),
453                cost: OperationCost {
454                    storage_loaded_bytes: 7,
455                    ..Default::default()
456                },
457            }
458        });
459
460        // Second function won't be executed and thus no costs added.
461        assert!(!executed);
462        assert_eq!(
463            mapped,
464            CostContext {
465                value: Err(()),
466                cost: OperationCost {
467                    storage_loaded_bytes: 3,
468                    ..Default::default()
469                },
470            }
471        );
472    }
473
474    #[test]
475    fn test_flat_map_err_second() {
476        let initial: CostResult<usize, ()> = CostContext {
477            value: Ok(75),
478            cost: OperationCost {
479                storage_loaded_bytes: 3,
480                ..Default::default()
481            },
482        };
483        let mut executed = false;
484        let mapped: CostResult<usize, ()> = initial.flat_map_ok(|_| {
485            executed = true;
486            CostContext {
487                value: Err(()),
488                cost: OperationCost {
489                    storage_loaded_bytes: 7,
490                    ..Default::default()
491                },
492            }
493        });
494
495        // Second function should be executed and costs should increase. Result is error
496        // though.
497        assert!(executed);
498        assert_eq!(
499            mapped,
500            CostContext {
501                value: Err(()),
502                cost: OperationCost {
503                    storage_loaded_bytes: 10,
504                    ..Default::default()
505                },
506            }
507        );
508    }
509
510    #[test]
511    fn test_flatten_nested_errors() {
512        let initial: CostResult<usize, &str> = CostContext {
513            value: Ok(75),
514            cost: OperationCost {
515                storage_loaded_bytes: 3,
516                ..Default::default()
517            },
518        };
519        // We use function that has nothing to do with costs but returns a result, we're
520        // trying to flatten nested errors inside CostContext.
521        let ok = initial.map_ok(|x| Ok(x + 25));
522        assert_eq!(ok.flatten().unwrap(), Ok(100));
523
524        let initial: CostResult<usize, &str> = CostContext {
525            value: Ok(75),
526            cost: OperationCost {
527                storage_loaded_bytes: 3,
528                ..Default::default()
529            },
530        };
531        let error_inner: CostResult<Result<usize, &str>, &str> = initial.map_ok(|_| Err("latter"));
532        assert_eq!(error_inner.flatten().unwrap(), Err("latter"));
533
534        let initial: CostResult<usize, &str> = CostContext {
535            value: Err("inner"),
536            cost: OperationCost {
537                storage_loaded_bytes: 3,
538                ..Default::default()
539            },
540        };
541        let error_inner: CostResult<Result<usize, &str>, &str> = initial.map_ok(|x| Ok(x + 25));
542        assert_eq!(error_inner.flatten().unwrap(), Err("inner"));
543    }
544
545    #[test]
546    fn test_wrap_fn_cost() {
547        // Imagine this one is loaded from storage.
548        let loaded_value = b"ayylmao";
549        let costs_ctx = loaded_value.wrap_fn_cost(|x| OperationCost {
550            seek_count: 1,
551            storage_loaded_bytes: x.len() as u64,
552            ..Default::default()
553        });
554        assert_eq!(
555            costs_ctx,
556            CostContext {
557                value: loaded_value,
558                cost: OperationCost {
559                    seek_count: 1,
560                    storage_loaded_bytes: 7,
561                    ..Default::default()
562                }
563            }
564        )
565    }
566
567    #[test]
568    fn test_map_err() {
569        let initial: CostResult<usize, ()> = CostContext {
570            value: Err(()),
571            cost: OperationCost {
572                storage_loaded_bytes: 3,
573                ..Default::default()
574            },
575        };
576
577        let mapped = initial.map_err(|_| "ayyerror");
578        assert_eq!(
579            mapped,
580            CostContext {
581                value: Err("ayyerror"),
582                cost: OperationCost {
583                    storage_loaded_bytes: 3,
584                    ..Default::default()
585                },
586            }
587        );
588    }
589}