src/DurableError.bend

mylsm-lsm-store@0.5.0.0 · 123 lines · 0 definitions

1import Base
2import mylsm-lsm-store@0.5.0.0/src/Manifest.bend as Manifest
3
4# Lists the structured durable error categories.
5type ErrorKind is Data:
6  NotFound{}
7  AlreadyExists{}
8  Busy{}
9  InvalidArgument{}
10  UnsupportedFormat{}
11  Corruption{}
12  ResourceLimit{}
13  Io{}
14  CommitUnknown{}
15
16# Carries operation, path, and host details with its category.
17type Error is Data:
18  Error{kind: ErrorKind, operation: String, path: String, host_code: U32, host_message: String}
19
20# Records the failure that ended a database open.
21type OpenCause is Data:
22  MissingPath{}
23  ExistingPath{}
24  Locked{}
25  InvalidInput{}
26  UnknownFormat{}
27  CorruptData{}
28  LimitReached{}
29  OpenHostFailure{code: U32, message: String}
30
31# Identifies whether a write failed before or after append.
32type WritePhase is Data:
33  BeforeAppend{}
34  AppendStarted{}
35
36# Records the codec or host failure behind a write result.
37type WriteCause is Data:
38  ValidationFailure{}
39  WriteLimitReached{}
40  WriteHostFailure{code: U32, message: String}
41
42# Builds an error with its structured host and operation details.
43def error(kind: ErrorKind, operation: String, path: String, code: U32, message: String) -> Error:
44  Error{kind, operation, path, code, message}
45
46# Maps open causes to their public error categories.
47def classify_open(cause: OpenCause, operation: String, path: String) -> Error:
48  match cause:
49    case MissingPath{}:
50      error(NotFound{}, operation, path, 0, "")
51    case ExistingPath{}:
52      error(AlreadyExists{}, operation, path, 0, "")
53    case Locked{}:
54      error(Busy{}, operation, path, 0, "")
55    case InvalidInput{}:
56      error(InvalidArgument{}, operation, path, 0, "")
57    case UnknownFormat{}:
58      error(UnsupportedFormat{}, operation, path, 0, "")
59    case CorruptData{}:
60      error(Corruption{}, operation, path, 0, "")
61    case LimitReached{}:
62      error(ResourceLimit{}, operation, path, 0, "")
63    case OpenHostFailure{code, message}:
64      error(Io{}, operation, path, code, message)
65
66def classify_recovery.limit(+code: U32, +message: String, +operation: String, +path: String) -> Error:
67  Bool.pick(Error, U32.is_eq(code, Manifest.parse_limit_error_code()),
68    error(ResourceLimit{}, operation, path, 0, "manifest size limit exceeded"),
69    classify_open(OpenHostFailure{code, message}, operation, path))
70
71def classify_recovery.corruption(+code: U32, +message: String, +operation: String, +path: String) -> Error:
72  Bool.pick(Error, U32.is_eq(code, Manifest.malformed_error_code()),
73    error(Corruption{}, operation, path, 0, "malformed manifest"),
74    classify_recovery.limit(code, message, operation, path))
75
76def classify_recovery.sst(+code: U32, +message: String, +operation: String, +path: String) -> Error:
77  Bool.pick(Error, U32.is_eq(code, 4294967292),
78    error(Corruption{}, operation, path, 0, "malformed SST"),
79    classify_recovery.corruption(code, message, operation, path))
80
81def classify_recovery.levels(+code: U32, +message: String, +operation: String, +path: String) -> Error:
82  Bool.pick(Error, U32.is_eq(code, 5),
83    error(Corruption{}, operation, path, 0, "invalid manifest names"),
84    classify_recovery.sst(code, message, operation, path))
85
86def classify_recovery.wal(+code: U32, +message: String, +operation: String, +path: String) -> Error:
87  Bool.pick(Error, U32.is_eq(code, 4294967290),
88    error(Corruption{}, operation, path, 0, "malformed WAL"),
89    classify_recovery.levels(code, message, operation, path))
90
91# Maps recovery failures to typed public errors.
92def classify_recovery(+code: U32, +message: String, +operation: String, +path: String) -> Error:
93  Bool.pick(Error, U32.is_eq(code, Manifest.unsupported_version_error_code()),
94    error(UnsupportedFormat{}, operation, path, 0, "unsupported manifest version"),
95    classify_recovery.wal(code, message, operation, path))
96
97# Classifies failures that occur before WAL append begins.
98def classify_write_before(cause: WriteCause, operation: String, path: String) -> Error:
99  match cause:
100    case ValidationFailure{}:
101      error(InvalidArgument{}, operation, path, 0, "")
102    case WriteLimitReached{}:
103      error(ResourceLimit{}, operation, path, 0, "")
104    case WriteHostFailure{code, message}:
105      error(Io{}, operation, path, code, message)
106
107# Classifies failures after append begins as CommitUnknown.
108def classify_write_after(cause: WriteCause, operation: String, path: String) -> Error:
109  match cause:
110    case ValidationFailure{}:
111      error(InvalidArgument{}, operation, path, 0, "")
112    case WriteLimitReached{}:
113      error(ResourceLimit{}, operation, path, 0, "")
114    case WriteHostFailure{code, message}:
115      error(CommitUnknown{}, operation, path, code, message)
116
117# Selects the write error category from its append stage.
118def classify_write(phase: WritePhase, cause: WriteCause, operation: String, path: String) -> Error:
119  match phase:
120    case BeforeAppend{}:
121      classify_write_before(cause, operation, path)
122    case AppendStarted{}:
123      classify_write_after(cause, operation, path)