src/DurableError.bend

mylsm-lsm-store@0.6.0.1 · 125 lines · 16 definitions

1import Base
2import mylsm-lsm-store@0.6.0.1/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)