~/bend-docscommunity

src/RecoverPure.bend source

src/RecoverPure.bend on the hub · documented module

import Baseimport ./MemTable.bend as MemTableimport ./Manifest.bend as Manifestimport ./Sstable.bend as Sstable# --- Flush-counter restore (pure) ---# Handle mid and in the pure recovery decisions.def mid_and(lhs: Bool, rhs: Bool) -> Bool:  match lhs:    case True{}:      rhs    case False{}:      False{}# Handle shape mid in the pure recovery decisions.def shape_mid(first: Bool, second: Bool, third: Bool) -> Bool:  mid_and(first, mid_and(second, third))# Table generations are compact decimal only. Unparsable middles are invalid# (no unary-dash legacy reads: no retrocompat).def gen_valid_read(parsed: Maybe<&2, Nat>) -> Bool:  match parsed:    case Some{n}:      True{}    case None{}:      False{}# Handle gen valid in the pure recovery decisions.def gen_valid(+middle: String) -> Bool:  gen_valid_read(Nat.read(middle))# Handle ov ok in the pure recovery decisions.def ov_ok(+big: Bool, +name: String, +ln: Nat) -> Bool:  match big:    case True{}:      shape_mid(String.starts_with(name, "l"), String.ends_with(name, ".tbl"), gen_valid(String.take(String.drop(name, 3n), Nat.sub(ln, 7n))))    case False{}:      False{}# Handle name shape in the pure recovery decisions.def name_shape(+name: String) -> Bool:  +ln = String.length(name)  ov_ok(Nat.is_le(7n, ln), name, ln)# Return the level name ok for the pure recovery decisions.def level_name_ok(+big: Bool, +name: String, +ln: Nat, +prefix: String, +prefix_len: Nat) -> Bool:  match big:    case False{}:      False{}    case True{}:      shape_mid(String.starts_with(name, prefix), String.ends_with(name, ".tbl"), gen_valid(String.take(String.drop(name, prefix_len), Nat.sub(ln, Nat.add(prefix_len, 4n)))))# Handle name shape level in the pure recovery decisions.def name_shape_level(+name: String, +level: Nat) -> Bool:  +prefix = "l" ++ Nat.show(level) ++ "-"  +prefix_len = String.length(prefix)  +ln = String.length(name)  level_name_ok(Nat.is_le(Nat.add(prefix_len, 4n), ln), name, ln, prefix, prefix_len)# Handle gen value read in the pure recovery decisions.def gen_value_read(parsed: Maybe<&2, Nat>) -> Nat:  match parsed:    case Some{n}:      n    case None{}:      0n# Handle gen value in the pure recovery decisions.def gen_value(+middle: String) -> Nat:  gen_value_read(Nat.read(middle))# Handle name gen in the pure recovery decisions.def name_gen(+name: String) -> Nat:  +ln = String.length(name)  gen_value(String.take(String.drop(name, 3n), Nat.sub(ln, 7n)))# Handle names ok in the pure recovery decisions.def names_ok(xs: List<&2, String>) -> Bool:  match xs:    case Nil{}:      True{}    case Con{h, t}:      mid_and(name_shape(h), names_ok(t))# Return the level names ok for the pure recovery decisions.def level_names_ok(+level: Nat, xs: List<&2, String>) -> Bool:  match xs:    case Nil{}:      True{}    case Con{h, t}:      mid_and(name_shape_level(h, level), level_names_ok(level, t))# Handle manifest names ok in the pure recovery decisions.def manifest_names_ok(levels: List<&2, List<&2, String>>, +level: Nat) -> Bool:  match levels:    case Nil{}:      True{}    case Con{names, rest}:      mid_and(level_names_ok(level, names), manifest_names_ok(rest, Nat.add(level, 1n)))# Handle names all in the pure recovery decisions.def names_all(lvls: List<&2, List<&2, String>>) -> List<&2, String>:  match lvls:    case Nil{}:      Nil{}    case Con{h, t}:      List.append(&2, String, h, names_all(t))# Handle cmax in the pure recovery decisions.def cmax(names: List<&2, String>, +cur: Nat) -> Nat:  match names:    case Nil{}:      cur    case Con{h, t}:      cmax(t, Nat.max(name_gen(h), cur))# Count gen in the pure recovery decisions.def count_gen(+lvls: List<&2, List<&2, String>>) -> Nat:  Nat.add(cmax(names_all(lvls), 0n), 1n)# Handle mfst lists in the pure recovery decisions.def mfst_lists(+mfst: Manifest.Manifest) -> List<&2, List<&2, String>>:  match mfst:    case Manifest.M{lvs}:      lvs# L0 may overlap by design. Every table within each level below L0 must have# a range disjoint from every peer; metadata makes this check independent of# SortedRun and avoids reparsing entries during recovery.def disjoint_with_table(+tbl: Sstable.Table, rest: List<&2, Sstable.Table>) -> Bool:  match rest:    case Nil{}:      True{}    case Con{+h, t}:      mid_and(Sstable.ranges_disjoint(tbl, h), disjoint_with_table(tbl, t))# Return the level pairwise disjoint for the pure recovery decisions.def level_pairwise_disjoint(tables: List<&2, Sstable.Table>) -> Bool:  match tables:    case Nil{}:      True{}    case Con{+h, +t}:      mid_and(disjoint_with_table(h, t), level_pairwise_disjoint(t))# Check lower-level levels disjoint for the pure recovery decisions.def lower_levels_disjoint(levels: List<&2, List<&2, Sstable.Table>>) -> Bool:  match levels:    case Nil{}:      True{}    case Con{+level, rest}:      mid_and(level_pairwise_disjoint(level), lower_levels_disjoint(rest))# Handle l1 plus disjoint in the pure recovery decisions.def l1_plus_disjoint(levels: List<&2, List<&2, Sstable.Table>>) -> Bool:  match levels:    case Nil{}:      True{}    case Con{l0, lower}:      lower_levels_disjoint(lower)# Handle needs flush cap in the pure recovery decisions.def needs_flush_cap(+mem: MemTable.MemTable, +cap: Nat) -> Bool:  Nat.is_lt(cap, MemTable.count(mem))