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