telega/dead_letter
Updates that crashed the chat instance handling them.
A handler that panics takes its chat instance down with it. The bot actor already notices (it monitors every instance it dispatched to) and answers the poller or the webhook for the update the instance never finished — but the update itself is gone, and with it any chance of finding out what the bot was asked to do.
A dead-letter queue keeps it. Give the bot a KeyValueStorage with
telega.with_dead_letters and every
update whose instance crashed is written under the dlq: prefix as the
raw JSON Telegram sent, together with the reason. Read them back with
telega.dead_letters, re-dispatch them with
telega.replay_dead_letters once the
bug is fixed, and drop them with [telega.drop_dead_letter].
let storage = storage.new(...)
telega.new(api_client)
|> telega.router(router)
|> telega.with_dead_letters(storage.dead_letters_from_storage(storage, ttl: None))
|> telega.start()
Writing is fire-and-forget in a spawned process: the bot actor is the only dispatcher a bot has, and a wedged storage backend must not be able to stop it. A write that fails is logged and the letter is lost — the queue is a debugging aid, not a durable inbox.
Entries are keyed by update_id, so a replayed update that crashes again
overwrites its own entry instead of growing the queue.
Types
One stored update, as read back from the queue.
pub type DeadLetter {
DeadLetter(key: String, update: types.Update, reason: String)
}
Constructors
-
DeadLetter(key: String, update: types.Update, reason: String)Arguments
- key
-
Storage key, e.g.
"dlq:123456". Pass it todrop_dead_letter. - update
-
The update, ready to be handed back to
telega.handle_update. - reason
-
Why the instance went down, as the BEAM reported it.
A place to put updates that crashed, and to read them back from.
Built from a KeyValueStorage with
storage.dead_letters_from_storage;
the backend’s own error type is flattened to a String here, because the
bot that holds one is already generic over three type parameters and the
queue is never on a path where the error is matched on.
pub opaque type DeadLetters
Values
pub fn drop(
letters letters: DeadLetters,
key key: String,
) -> Result(Nil, String)
Forget one letter.
pub fn list(
letters letters: DeadLetters,
) -> Result(#(List(DeadLetter), List(String)), String)
Every stored letter, oldest update_id first.
A payload that will not decode is reported in the Error list rather than
silently skipped — a queue that quietly drops what it cannot read is worse
than no queue.
pub fn new(
put put: fn(String, String) -> Result(Nil, String),
keys keys: fn() -> Result(List(String), String),
read read: fn(String) -> Result(option.Option(String), String),
drop drop: fn(String) -> Result(Nil, String),
) -> DeadLetters
Build a queue out of four storage primitives. Backends live in
telega/storage; this is the seam a custom one plugs into.
pub fn read(
letters letters: DeadLetters,
key key: String,
) -> Result(DeadLetter, String)
Read one letter back by its storage key.
pub fn record(
letters letters: DeadLetters,
update update: types.Update,
reason reason: String,
) -> Result(Nil, String)
Store one update. Called by the bot actor when a chat instance goes down with work still in flight.