Repository navigation
chore: release v0.5.0 - #278
Merged
Merged
Conversation
Contributor
QualOps Code Quality AnalysisStatus: Summary
🟠 High Issues (1)
📊 Full ReportPowered by QualOps |
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release v0.5.0
Migration note for DLQ consumers. The shape of DLQ entries changes in
this release; the SDK API, retry-stream entries, handler-side
_retry_count/_original_message_id, stream key names and per-handlertrimming do not. If you read DLQ entries yourself (
XRANGE, re-drivescripts, monitoring) or read the dict passed to
on_dlq:_dlq_retries, not_retry_count(which is now
"0"on every DLQ entry);_dlq_source,_dlq_handler,_dlq_at,_dlq_reasonkeys;base64-decoding
_dlq_raw_b64, andon_dlqreceives that decoded dict;__data__field (only possible from a non-eggaiproducer) are no longer retried at all; they are dead-lettered as poison on
the first reclaim.
The
on_dlq(body, msg_id, retry_count)signature and itsretry_countargument are unchanged.
Added
RedisTransport: newdlq_channelsubscribe option. Dead-letters into ONEstream you name instead of the per-handler
{channel}.{handler_suffix}.dlq,so a single consumer can subscribe to every handler's (or every service's)
failures with a plain
@agent.subscribe(channel=dlq, group_start="0").Accepts a
Channelor a topic name (namespaced likeChannel(name)) onAgent.subscribe/Channel.subscribe; a full key onRedisTransport.subscribe. Requiresretry_on_idle_msand a non-Nonemax_retries; rejects the subscribed channel and the handler's retry streamas targets. The
.retrystream stays per-handler (Redis transport: shared *.retry stream broadcasts a single group's failures to every other handler on the same channel #225) — only the terminalsink is shared. A shared DLQ is written without
MAXLENby default(
retry_max_lenkeeps applying to retry streams and per-handler DLQs):stream-wide trimming would let one writer's cap delete other writers'
unconsumed dead letters.
RedisTransport(dlq_max_len=...)is the opt-inhard ceiling for a shared DLQ. Default behaviour without
dlq_channelisunchanged.
existing
_retry_count/_original_message_id:_dlq_source(the channelkey the handler subscribed to),
_dlq_handler(handler suffix / consumergroup),
_dlq_at(ISO-8601 UTC) and_dlq_reason("max_retries"or"poison") and_dlq_retries(how many retries actually ran). Additive:typed models ignore the extra keys, as they already do for
_retry_count.On an entry dead-lettered a second time (by a DLQ consumer that gave up)
the origin keys
_dlq_source/_dlq_handler/_dlq_atkeep the firstfailure, while
_dlq_reason/_dlq_retriesdescribe the latest hop.Changed
_retry_countis reset to"0"on the DLQ write (the exhausted budgetmoves to
_dlq_retries). A DLQ entry is also a first delivery to whateverconsumes the DLQ; carrying the exceeded count over meant a DLQ consumer with
its own
retry_on_idle_msgot zero retries and, with backoff, an escalatedfirst reclaim. Anything that read
_retry_countoff a DLQ entry should read_dlq_retries; theon_dlqcallback'sretry_countargument is unchanged.the DLQ verbatim. They are wrapped in a well-formed envelope whose body holds
the
_dlq_*fields,_original_message_id, and the original bytes as_dlq_raw_b64, so a DLQ subscriber always receives a decodable JSON objectinstead of raw bytes. Envelope headers are preserved when only the body was
unusable. Re-drive scripts that handled raw poison entries should read
_dlq_raw_b64. Consequentlyon_dlqnow receives that decoded dict forpoison entries too, instead of the raw
{b"__data__": bytes}fields.Agent.subscribe/Channel.subscribereject adlq_channelstring thatalready starts with
EGGAI_NAMESPACE(e.g. a pastedchannel.get_name()),which would otherwise be namespaced twice and route dead letters to an
unwatched
<ns>.<ns>.dlq. Pass the bare topic name or aChannel.RedisTransport.subscriberejectsretry_on_idle_mswithout a consumergroup (
handler_id=/group=): the reclaimer works on a group's PEL, sowithout one every reclaim cycle just errored.
Agent/Channelalways set agroup; this only affects direct transport callers.
dlq_channelrejects any key ending in.retry, not just the handler's ownretry stream: every
.retrystream is auto-consumed by some handler, sodead-lettering into one would feed that handler's retry loop. The check runs
before the broker subscriber is registered.
Fixed
PendingReclaimer: a pending entry with a parseable envelope but a non-objectJSON body (e.g. a published list), a non-integer
_retry_count, or no__data__field at all (non-eggai producer) escaped the poison handling. Thefirst two raised outside the guarded parse and aborted the whole reclaim
cycle after
XCLAIM, head-of-line blocking every later pending entry; thethird ping-ponged between main and retry stream forever with a retry count
that could never grow. All three are now dead-lettered as poison; in
particular, entries without
__data__are no longer retried at all.After merging, the release will be automatically tagged and published to PyPI.