1/**
2 * Defines the structured errors used by the unstable cluster runtime.
3 *
4 * These tagged, schema-backed errors describe failures at routing, runner
5 * membership, serialization, persistence, mailbox capacity, and duplicate
6 * envelope boundaries. Cluster clients, runners, and storage adapters use these
7 * shared error values to report failures through typed Effect errors.
8 *
9 * @stability unstable
10 * @since 4.0.0
11 */
12import * as Cause from "../Cause.ts"
13import * as Effect from "../Effect.ts"
14import { hasProperty, isTagged } from "../Predicate.ts"
15import * as Schema from "../Schema.ts"
16import { EntityAddress } from "./EntityAddress.ts"
17import { RunnerAddress } from "./RunnerAddress.ts"
18import { SnowflakeFromString } from "./Snowflake.ts"
19
20const TypeId = "~effect/cluster/ClusterError"
21
22/**
23 * Represents an error that occurs when a Runner receives a message for an entity
24 * that is not assigned to the receiving runner.
25 *
26 * @stability unstable
27 * @category errors
28 * @since 4.0.0
29 */
30export class EntityNotAssignedToRunner
31 extends Schema.Error<EntityNotAssignedToRunner>(`${TypeId}/EntityNotAssignedToRunner`)({
32 _tag: Schema.tag("EntityNotAssignedToRunner"),
33 address: EntityAddress
34 })
35{
36 /**
37 * Marks this value as a cluster error for runtime guards.
38 *
39 * @stability unstable
40 * @since 4.0.0
41 */
42 readonly [TypeId] = TypeId
43
44 /**
45 * Returns `true` when the value is an `EntityNotAssignedToRunner` error.
46 *
47 * @stability unstable
48 * @since 4.0.0
49 */
50 static is(u: unknown): u is EntityNotAssignedToRunner {
51 return hasProperty(u, TypeId) && isTagged(u, "EntityNotAssignedToRunner")
52 }
53}
54
55/**
56 * Represents an error that occurs when a message fails at a schema
57 * serialization or deserialization boundary.
58 *
59 * **Details**
60 *
61 * `cause` carries the underlying failure. `refail` maps encode and decode
62 * failures into `MalformedMessage` values.
63 *
64 * @stability unstable
65 * @category errors
66 * @since 4.0.0
67 */
68export class MalformedMessage extends Schema.Error<MalformedMessage>(`${TypeId}/MalformedMessage`)({
69 _tag: Schema.tag("MalformedMessage"),
70 cause: Schema.Defect()
71}) {
72 /**
73 * Marks this value as a cluster error for runtime guards.
74 *
75 * @stability unstable
76 * @since 4.0.0
77 */
78 readonly [TypeId] = TypeId
79
80 /**
81 * Returns `true` when the value is a `MalformedMessage` error.
82 *
83 * @stability unstable
84 * @since 4.0.0
85 */
86 static is(u: unknown): u is MalformedMessage {
87 return hasProperty(u, TypeId) && isTagged(u, "MalformedMessage")
88 }
89
90 /**
91 * Maps failures from the supplied effect into `MalformedMessage` errors.
92 *
93 * @stability unstable
94 * @since 4.0.0
95 */
96 static refail: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<
97 A,
98 MalformedMessage,
99 R
100 > = Effect.mapError((cause) => new MalformedMessage({ cause }))
101}
102
103/**
104 * Represents an error that occurs when a message fails to be persisted into
105 * cluster's mailbox storage.
106 *
107 * @stability unstable
108 * @category errors
109 * @since 4.0.0
110 */
111export class PersistenceError extends Schema.Error<PersistenceError>(`${TypeId}/PersistenceError`)({
112 _tag: Schema.tag("PersistenceError"),
113 cause: Schema.Defect()
114}) {
115 /**
116 * Marks this value as a cluster error for runtime guards.
117 *
118 * @stability unstable
119 * @since 4.0.0
120 */
121 readonly [TypeId] = TypeId
122
123 /**
124 * Maps failures from the supplied effect into `PersistenceError` values.
125 *
126 * @stability unstable
127 * @since 4.0.0
128 */
129 static refail<A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, PersistenceError, R> {
130 return Effect.catchCause(effect, (cause) => Effect.fail(new PersistenceError({ cause: Cause.squash(cause) })))
131 }
132}
133
134/**
135 * Represents an error that occurs when a Runner is not registered with the shard
136 * manager.
137 *
138 * @stability unstable
139 * @category errors
140 * @since 4.0.0
141 */
142export class RunnerNotRegistered extends Schema.Error<RunnerNotRegistered>(`${TypeId}/RunnerNotRegistered`)({
143 _tag: Schema.tag("RunnerNotRegistered"),
144 address: RunnerAddress
145}) {
146 /**
147 * Marks this value as a cluster error for runtime guards.
148 *
149 * @stability unstable
150 * @since 4.0.0
151 */
152 readonly [TypeId] = TypeId
153}
154
155/**
156 * Represents an error that occurs when a Runner is unresponsive.
157 *
158 * @stability unstable
159 * @category errors
160 * @since 4.0.0
161 */
162export class RunnerUnavailable extends Schema.Error<RunnerUnavailable>(`${TypeId}/RunnerUnavailable`)({
163 _tag: Schema.tag("RunnerUnavailable"),
164 address: RunnerAddress
165}) {
166 /**
167 * Marks this value as a cluster error for runtime guards.
168 *
169 * @stability unstable
170 * @since 4.0.0
171 */
172 readonly [TypeId] = TypeId
173
174 /**
175 * Returns `true` when the value is a `RunnerUnavailable` error.
176 *
177 * @stability unstable
178 * @since 4.0.0
179 */
180 static is(u: unknown): u is RunnerUnavailable {
181 return hasProperty(u, TypeId) && isTagged(u, "RunnerUnavailable")
182 }
183}
184
185/**
186 * Represents an error that occurs when the entity mailbox is full.
187 *
188 * **Details**
189 *
190 * Carries the `address` whose bounded mailbox is at capacity. It is also used
191 * when the runner has reached `ShardingConfig.maxResidentEntities` and cannot
192 * spawn a new entity for the `address`.
193 *
194 * **Gotchas**
195 *
196 * Volatile requests fail immediately. Persisted or durable messages are retried
197 * or resumed from storage when the mailbox is full.
198 *
199 * @stability unstable
200 * @category errors
201 * @since 4.0.0
202 */
203export class MailboxFull extends Schema.Error<MailboxFull>(`${TypeId}/MailboxFull`)({
204 _tag: Schema.tag("MailboxFull"),
205 address: EntityAddress
206}) {
207 /**
208 * Marks this value as a cluster error for runtime guards.
209 *
210 * @stability unstable
211 * @since 4.0.0
212 */
213 readonly [TypeId] = TypeId
214
215 /**
216 * Returns `true` when the value is a `MailboxFull` error.
217 *
218 * @stability unstable
219 * @since 4.0.0
220 */
221 static is(u: unknown): u is MailboxFull {
222 return hasProperty(u, TypeId) && isTagged(u, "MailboxFull")
223 }
224}
225
226/**
227 * Represents an error that occurs when the same request envelope is already
228 * being processed.
229 *
230 * **Details**
231 *
232 * Carries the `address` and `envelopeId` for the affected request envelope.
233 *
234 * @stability unstable
235 * @category errors
236 * @since 4.0.0
237 */
238export class AlreadyProcessingMessage
239 extends Schema.Error<AlreadyProcessingMessage>(`${TypeId}/AlreadyProcessingMessage`)({
240 _tag: Schema.tag("AlreadyProcessingMessage"),
241 envelopeId: SnowflakeFromString,
242 address: EntityAddress
243 })
244{
245 /**
246 * Marks this value as a cluster error for runtime guards.
247 *
248 * @stability unstable
249 * @since 4.0.0
250 */
251 readonly [TypeId] = TypeId
252
253 /**
254 * Returns `true` when the value is an `AlreadyProcessingMessage` error.
255 *
256 * @stability unstable
257 * @since 4.0.0
258 */
259 static is(u: unknown): u is AlreadyProcessingMessage {
260 return hasProperty(u, TypeId) && isTagged(u, "AlreadyProcessingMessage")
261 }
262}