Concurrent Submit | zhenzhen-image-g-v2.5-lowprice
Thirty Image G v2.5 Jobs at Once, Without Freezing the Graph That Made Them
- image1
- image2
- image3
- image4
- image5
- image6
- image7
- image8
- image9
- image10
- image11
- image12
- image13
- image14
- image15
- api_config
- task
A single lowprice call is a submit, a poll and a download. Thirty of them in series is a graph that looks like it hung. This node exists to break that chain: it's the submit half of the pair, it does the same call as zhenzhen-image-g-v2.5-lowprice, and it returns a handle instead of an image so the next one can start immediately.
Thirty workers, one shared pool, per the pack's own description of it. The original node is untouched - this is an additive twin, so any workflow you already have keeps working and you can adopt the concurrent version one node at a time.
The shape of it
Inputs are an exact copy of the standalone node: prompt, resolution, size and nsfw_check are still required, and image1–image15, api_config, skip_error and seed are still optional. Same validation, too - the wrapper reuses the original node's validator, so a 5,001-character prompt or a bogus ratio gets rejected at submission rather than burning a pool slot.
The only new thing is the output: one task socket, typed COMFLY_IMAGE_FUTURE. It is not an image and it will not connect to Save Image. Wire it into the collector node, Concurrent Collect Images (30) (ComflyConcurrent_Image_Await in the class list), which takes up to thirty of these task handles plus a failure_mode setting, waits for the whole set, and then emits thirty image_* outputs plus a status string.
The slots are positional. Task socket number four produces image_4, regardless of which job finished first. That's the feature - batching cloud calls without a post-processing spreadsheet.
failure_mode picks the mood. fail_fast raises on the first failure and cancels what's still queued. placeholder fills the dead slot with a blank image and reports it in status. Use fail_fast while you're tuning prompts; switch to placeholder once you're running unattended batches.
Where the concurrency actually lives
Not in the nodes you can see. The pack builds two bounded thread pools at import - images and video get separate ones, because a video job can occupy a slot for minutes and you don't want that starving your images. The image pool is thirty workers with a matching pending allowance, and each submit node just queues and returns.
Both numbers are environment variables, read when ComfyUI loads:
export COMFLY_IMAGE_CONCURRENCY=10 # worker threads (default 30)
export COMFLY_IMAGE_PENDING=10 # extra queued slots (default = workers)
Drop the worker count if the relay starts answering with 429s - this is a shared reseller endpoint with the provider's own rate limits behind it, and thirty-wide is a good way to discover them. Values are clamped to 1–128, and a typo just prints a line and reverts to the default.
Two things that surprise people
First: api_config. Every submit node in the batch needs it. The Settings node's api_config output fans out to as many inputs as you like, one wire each, so it's one node and ten wires - but if you forget one, that slot fails inside its worker thread rather than at submission time, and you'll be reading the collector's status string to find out which.
Second: the seed. It's still cache-only, and the pack leans on that hard - with seed on fixed and nothing else changed, ComfyUI serves the cached result and the task is never re-submitted. That's what makes it safe to re-run a big graph after tweaking one prompt: the finished jobs don't get re-billed. Flip the seed to randomize and every slot fires again. This is also the general ComfyUI caching behaviour worth knowing about (control_after_generate controls what happens to the number after a run, which is why people lose good seeds), and it applies here exactly as it does to a KSampler.
Install
Standard for the pack - Manager, search Comfyui-zhenzhen, or:
cd ComfyUI/custom_nodes && git clone https://github.com/T8mars/Comfyui-zhenzhen
Restart, and both the standalone and concurrent versions register. Nothing to download beyond Python packages, and the pool is concurrent.futures - no queue server, no extra process.
Costs, plainly
n doesn't exist on this node - one task, one image - but each submit node is a billed call, so a 12-slot graph is twelve charges, thirty of them in flight at peak. The pool will happily queue more than you meant to spend. Interrupting cancels the wait and the queued futures; it cannot un-spend a credit on a task the server already accepted.
Inputs (22)
| Name | Type | Default | Description |
|---|---|---|---|
| prompt | STRING | — | |
| resolution | COMBO | 1k | 3 options: 1k, 2k, 4k |
| size | COMBO | 16:9 | 16 options: auto, 1:1, 1:3, 3:1, 16:9, 9:16, +10 |
| nsfw_check | BOOLEAN | false | — |
| image1opt | IMAGE | — | |
| image2opt | IMAGE | — | |
| image3opt | IMAGE | — | |
| image4opt | IMAGE | — | |
| image5opt | IMAGE | — | |
| image6opt | IMAGE | — | |
| image7opt | IMAGE | — | |
| image8opt | IMAGE | — | |
| image9opt | IMAGE | — | |
| image10opt | IMAGE | — | |
| image11opt | IMAGE | — | |
| image12opt | IMAGE | — | |
| image13opt | IMAGE | — | |
| image14opt | IMAGE | — | |
| image15opt | IMAGE | — | |
| api_configopt | ZHENZHEN_SEEDANCE2_CONFIG | — | |
| skip_erroropt | BOOLEAN | false | — |
| seedopt | INT | 00–18446744073709550000 | ComfyUI cache seed only; it is not sent to Image G v2.5. Fixed reuses the cached result. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| task | COMFLY_IMAGE_FUTURE | — |