Ruby Thread and Queue: four workers submit Wan 3.0 clips

Ruby worker pool for POST /v1/videos: a Queue of shots, four Threads, Net::HTTP and one Idempotency-Key per shot. Tested on Ruby with a stub server.

4 min readSume
All posts

Ruby has no built-in pool class, but Queue plus a handful of Threads is all a submit loop needs. Put the shot numbers on a queue, start four threads that each pop until the queue is empty, and collect [shot, status] pairs on a second queue. Queue is thread-safe, so no lock is required.

This sample submits 12 five-second Wan 3.0 clips at 480p. It uses only net/http, json and uri, and it was run against a local stand-in server that answers 202, not the live API. Set SUME_BASE (for example https://api.sume.com) and SUME_API_KEY.

The code (22 lines)

jobs.pop(true) is the non-blocking form. It raises ThreadError on an empty queue, which the rescue nil turns into the loop's exit condition. Without the true argument the thread would block forever once the queue drains.

require "net/http"
require "json"
require "uri"

uri = URI("#{ENV.fetch('SUME_BASE')}/v1/videos")
jobs = Queue.new
(0...12).each { |i| jobs << i }
results = Queue.new
workers = Array.new(4) do  # 4 threads = Pro processing concurrency
  Thread.new do
    until (i = (jobs.pop(true) rescue nil)).nil?
      req = Net::HTTP::Post.new(uri, "x-api-key" => ENV.fetch("SUME_API_KEY"),
                                "content-type" => "application/json",
                                "Idempotency-Key" => format("trailer-v3-shot-%02d", i))
      req.body = { model: "wan-3.0", prompt: "shot #{i}", duration: 5, resolution: "480p" }.to_json
      res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") { |h| h.request(req) }
      results << [i, res.code.to_i]
    end
  end
end
workers.each(&:join)
puts Array.new(results.size) { results.pop }.sort.map(&:last).inspect

Idempotency per shot

The key trailer-v3-shot-07 is built from the shot number and nothing random. Sume documents that an Idempotency-Key replay returns the original job, so a crashed run can simply be restarted. A key reused with a different body is a conflict (409 idempotency_conflict), so change the key if you change the prompt or settings.

Four threads, not forty

Each plan has a processing concurrency (Free 1, Pro 4, Startup 8, Scale 20) and a larger accepted capacity (6, 24, 48, 120). More threads than processing seats just move jobs from your queue into Sume's, and past the accepted capacity you get 429 queue_full. Four is the Pro number and a safe default.

A 429 or 503 in the printed list means that shot needs another attempt with the same key. Retrying a POST without a key is the one thing to avoid.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume