Skip to content

fix(Core): guard SysvProcessor message size and handle oversized messages in BatchJob - #9689

Open
cy-yun wants to merge 1 commit into
mainfrom
fix/core-batch-poison-message
Open

cy-yun wants to merge 1 commit into
mainfrom
fix/core-batch-poison-message

Conversation

@cy-yun

@cy-yun cy-yun commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Fixes #9394

Problem

When kernel.msgmax is raised above the standard default of 8192 bytes (e.g. kernel.msgmax = 65536 in containerized or high-throughput environments), SysvProcessor::submit()'s initial call to msg_send(..., self::$typeDirect, $item) succeeds for serialized items between 8193 and msgmax bytes.

However, BatchJob::run() calls msg_receive($q, 0, $type, 8192, $message, true, 0, $errorcode) with a hardcoded buffer limit of 8192 bytes and without MSG_NOERROR. Under System V IPC msgrcv(2), when a message in the queue exceeds msgsz and MSG_NOERROR is not set in msgflg, the syscall fails with E2BIG ($errorcode === 7 / PCNTL_E2BIG), leaving the message unconsumed at the head of the queue. Because the queue is not empty, subsequent msg_receive calls return immediately with E2BIG, causing a 100% CPU busy-loop and indefinitely blocking all subsequent messages.

Solution

  1. SysvProcessor:
    • Added const MAX_DIRECT_SIZE = 8192.
    • Before attempting msg_send(..., self::$typeDirect, ...), check if strlen($serialized) <= self::MAX_DIRECT_SIZE.
    • If the serialized item exceeds 8192 bytes, immediately invoke the temporary file fallback (self::$typeFile) instead of pushing an oversized direct message to the queue, reusing $serialized and ensuring direct messages never exceed 8192 bytes.
  2. BatchJob:
    • Added const MAX_MESSAGE_SIZE = 8192.
    • In run(), when msg_receive fails with E2BIG (checked against PCNTL_E2BIG, SOCKET_E2BIG, MSG_E2BIG, or POSIX errno 7), drain the poison message via drainOversizedMessage($q) using MSG_IPC_NOWAIT | MSG_NOERROR without unserializing. This consumes and purges the poison message from the queue head, preventing CPU starvation.
  3. Tests & Style Compliance:
    • Added boundary test cases in SysvProcessorTest::items() for items exceeding 8192 bytes.
    • Added unit tests in BatchJobTest for isMsgTooBig() and drainOversizedMessage().
    • Verified that all unit tests in Core/tests/Unit pass (537 tests, 856 assertions, 100% pass).
    • Validated code style with php-tools cs-fixer (Found 0 of 471 files that can be fixed) and phpcs (0 errors, 0 warnings).

@cy-yun
cy-yun requested a review from a team as a code owner September 14, 2026 21:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Core/Batch: hardcoded 8192-byte msg_receive() buffer causes a CPU-spin poison message when kernel.msgmax is raised above the default

1 participant