Repository navigation
Workers hand back a job they claimed while shutting down, WebSocket clients stop
reconnecting to a server that keeps closing them, and gRPC errors for a lost
lease or an exhausted plan quota carry the same type and code as their REST
counterparts. One change affects code that catches rate-limit errors from gRPC
calls; read "Changed" before upgrading.
Changed
- A gRPC
RESOURCE_EXHAUSTEDfor a plan limit is nowQuotaExceededError.
Messages such as "Daily job limit reached" or "queues limit reached (1/1)"
share theRESOURCE_EXHAUSTEDstatus with per-second rate limits, and the gRPC
client mapped all of them toRateLimitError. When the status has no
retry-aftertrailer and the message says that a limit was reached, the client
now raisesQuotaExceededError, the error REST raises for a quota. It is a
subclass ofRateLimitError, soexcept RateLimitErrorkeeps catching it, but
it is not retryable and carries noretry_after: a cap does not clear after a
delay. A real rate limit stays aRateLimitError, and itsretry_afteris now
read from theretry-aftertrailer when the status carries one. CreateScheduleParams.queue_nameandWorkflowJobDefinition.queue_name
accept at most 100 characters (they accepted 255). The API rejects anything
longer than 100 with a 400, the same cap asPOST /jobs, so a longer name now
fails when the model is built instead of when the request is sent.
UpdateScheduleParams.queue_nameis unchanged because the update route does not
apply a new queue name.
Fixed
- A worker no longer drops a job it claims while
stop()is running. Backend
0.1.126 and later does not requeue a job whose lease is still live when the
worker deregisters, so a job claimed afterstop()had ended its drain window
stayed inprocessinguntil the lease expired. The sync and async workers now
fail such a job ("Worker stopped before the job started") instead of leaving
it unstarted: it is rescheduled whenretry_count < max_retriesand
dead-lettered otherwise, the same rule as a handler failure, andjob:failed
is emitted. The handler does not run. stop()waits for a claim that is already in flight. A claim request on the
wire whenstop()started returns jobs this worker holds leases for.stop()
now waits for it so those jobs join the drain. The wait ends at the shutdown
deadline, so a claim that hangs or retries cannot holdstop()past
shutdown_timeout; jobs it returns later are failed as above. When the sync
stop()runs inside a signal handler on the thread that is blocked in the claim
call, it does not wait for itself.- WebSocket clients stop reconnecting after repeated accept-then-close.
WebSocketClient,AsyncWebSocketClientand the WebSocket transport of
SpooledRealtimereset the attempt counter as soon as the upgrade succeeded.
A server that accepted the connection and closed it at once (close 1011 when the
event channel cannot be opened) therefore refilled the budget on every cycle
and the client reconnected forever. The counter is now reset only after a socket
stayed up for 10 seconds (STABLE_CONNECTION_SECONDS); a socket that closes
sooner keeps counting towardmax_reconnect_attempts, and the client gives up
when the budget is spent. An explicitdisconnect()still starts with a fresh
budget. - A lost lease over gRPC is
LEASE_EXPIRED. UnaryComplete,Failand
RenewLeasereport a lost lease asFAILED_PRECONDITIONwith the text
"Lease expired", or the text saying thatlease_iddoes not match the job's
current lease, and no error code. The client mapped it to aConflictError
with the generic codeconflict, sois_lease_expiredwas false and the worker
kept the handler running. It now raisesConflictErrorwith codeLEASE_EXPIRED, so a worker
stops the handler and sends no ack, as it does for REST. Other
FAILED_PRECONDITIONanswers, such as "Job is already completed", are still a
plainConflictError.
Added
ProcessResponse.error_code: the error code of an in-band ProcessJobs error
(for exampleLEASE_EXPIRED), so callers can branch on it instead of parsing
ProcessResponse.error, which keeps the format"<code>: <message>".