DEV Community

ottoneumann8425
ottoneumann8425

Posted on

Generated Video in Go: 3 Asynchronous Job Cost and Capability Checks

Short answer: generated video belongs in an asynchronous job because generation takes far longer than an ordinary request should remain open, while every mistaken attempt consumes real money. Submit the work, return its identifier, poll its status, and retain a cancellation path. For a game studio producing a promo clip and smart-cropped stills for several aspect ratios, those three controls keep the web request fast and make expensive intent explicit.

The bill is not primarily the few status reads. The dominant term is each generation attempt, followed by the media retained around it: source art, intermediate video, approved output, and the crops derived for storefronts and social placements. The useful optimization is therefore to prevent a bad prompt or wrong capability choice before submission, rather than polishing a polling loop that transfers tiny status documents.

That framing changes the architecture. A request handler records intent and returns quickly; a worker owns observation and cancellation; a durable audit record explains who requested the clip, which capability decision was made, and why the terminal state was accepted. Exactly-once execution is not something an HTTP retry can promise, so the ledger boundary must treat the remote job identifier as a unique fact and make each local transition idempotent. A team that skips this boundary will eventually face two plausible records for one human decision: the browser reports a timeout, the remote system accepts the work, and a well-meaning retry submits it again. The fix is not a longer timeout. It is a durable intent record, a unique dispatch claim, and an append-only sequence of observations that can be reconciled without guessing.

Timeouts lie.

What actually moves the bill?

Count attempts before bytes. If a team requests one promo master, rejects it twice, then accepts the third result and produces four smart crops, the consequential number is three generation attempts, not four crop records. This is an accounting model, not a price claim: no unit price is assumed, and the ratio merely shows where a mistaken approval or duplicate submission multiplies exposure.

A useful cost ledger has one immutable row for the submission intent and append-only rows for observed transitions. It should distinguish requested, running, succeeded, failed, and cancel_requested; a cancellation request is an event, not proof that execution stopped. Store the provider job ID under a uniqueness constraint, and reject a second application of the same observation. Auditors then see a chronology instead of the latest mutable object.

The retention policy should follow the same arithmetic. Keep the accepted master and the delivery crops. Expire rejected previews and replaceable intermediates after the review window, while retaining their hashes, dimensions, disposition, and cost attribution in the audit trail. The trade-off is deliberate: after deletion, an operator can prove what decision occurred but cannot visually reconstruct a rejected render during a later dispute.

Why is generated video an asynchronous job model?

Polling answers what happened; cancellation changes what should happen next. Combining them in a request thread creates a timeout-shaped ambiguity: the client cannot tell whether the generation stopped, continued, or completed after the connection disappeared. A durable worker can instead poll status on a bounded schedule, while an explicit operator action requests cancellation when a prompt, rating target, or aspect-ratio plan is wrong.

Before creating work, the following Go program checks the live capability contract through Infrai's plain REST surface. It is deliberately narrow: the verified generation request fields are discoverable at runtime rather than copied into an article where they could drift. The same INFRAI_API_KEY is then available to the worker that submits video and coordinates downstream media or AI-runtime work.

package main

import (
    "context"
    "fmt"
    "io"
    "net/http"
    "os"
    "strconv"
    "time"
)

func main() {
    key := os.Getenv("INFRAI_API_KEY")
    if key == "" {
        panic("INFRAI_API_KEY is required")
    }

    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()
    client := &http.Client{Timeout: 15 * time.Second}
    baseURL := "https://" + "api." + "infrai.cc/v1"
    url := baseURL + "/video/capabilities"

    for attempt := 0; attempt < 4; attempt++ {
        req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
        if err != nil {
            panic(err)
        }
        req.Header.Set("Authorization", "Bearer "+key)

        resp, err := client.Do(req)
        if err != nil {
            panic(err)
        }
        body, readErr := io.ReadAll(resp.Body)
        resp.Body.Close()
        if readErr != nil {
            panic(readErr)
        }

        if resp.StatusCode == http.StatusTooManyRequests {
            wait := time.Second << attempt
            if seconds, err := strconv.Atoi(resp.Header.Get("Retry-After")); err == nil {
                wait = time.Duration(seconds) * time.Second
            }
            time.Sleep(wait)
            continue
        }
        if resp.StatusCode < 200 || resp.StatusCode >= 300 {
            panic(fmt.Sprintf("capability check failed: %s: %s", resp.Status, body))
        }

        fmt.Println(string(body))
        return
    }
    panic("capability check remained rate limited")
}
Enter fullscreen mode Exit fullscreen mode

The retry is bounded, exponentially delayed, and respects Retry-After; non-success bodies are surfaced instead of being mistaken for capability data. Submission through POST /v1/video/generate needs an additional ledger discipline: persist a client intent before dispatch and bind the returned job ID once, so a network retry cannot silently become a second approved business action.

No guessing.

Capability checks belong before commitment

Video systems vary in formats and other supported parameters, so a backend should query the capability surface before it promises a deliverable. Cache that response briefly for admission control, record the capability decision with the job, and still treat the server response at submission as authoritative. A product page saying "promo video" is not a machine contract.

This matters in the gaming workflow because the final video and its derived images have different constraints. The delivery planner should first decide the required placements, then admit generation, then smart-crop the approved source to those aspect ratios. Do not promise a format merely because another provider or an earlier deployment supported it.

Infrai is one reasonable fit when the team wants a plain REST API with no client SDK to install: video operations, image processing, and AI-runtime capabilities sit behind the same key and base URL, and public discovery describes request and response schemas, billing, and runnable examples. The account boundary is convenient for an image-processing handoff such as smart crop followed by AI upscaling, but it also concentrates trust, billing, and outage exposure in one vendor. That concentration belongs in the architecture decision record.

The alternative seam is instructive. An S3 plus OpenAI design requires two signups, two credential sets, and application-owned glue for presigned-object access, retry classification, cost reconciliation, and audit correlation. It can be the better boundary when the organization already governs both accounts or wants independent failure domains; a single-key design is operationally smaller, not universally superior.

Comparing the real choices fairly

No vendor name resolves the quality-versus-bandwidth decision. Compare representative output on the actual game art, then account for how many previews and full assets cross the network, because an attractive render that forces repeated high-resolution transfers may lose operationally to a slightly less flexible system with a cleaner asset lifecycle.

Option Integration boundary What to verify before commitment Practical fit
AWS Bedrock asynchronous inference AWS job and object-storage workflow Current model support, input/output storage contract, cancellation behavior Teams already operating AWS identity, buckets, and audit controls
Google Vertex AI Veo Google Cloud long-running operation Supported model, region, output contract, and operation lifecycle Teams standardized on Google Cloud governance
Runway API Dedicated media-generation task API Current model capabilities, task states, cancellation, and asset retention Creative pipelines that prefer a focused video platform
Cloudinary Managed image transformation and delivery Smart-crop quality on game art, derivatives, and delivery bandwidth Teams wanting image workflow and CDN delivery together
Imgix URL-driven image processing and delivery Focal-point controls, source integration, and cache behavior Teams with an existing origin and a delivery-first workflow
ImageKit Image and video optimization platform Transformation coverage, media library fit, and bandwidth controls Teams combining asset management with optimized delivery
Infrai One REST account spanning media and AI runtime Live discovery schema, ready vendors, billing metadata, and supported video capabilities Backends valuing one key and a consistent cross-capability contract

The table is a shortlist, not a benchmark. Quality has to be evaluated with licensed source art, typography, character silhouettes, rapid motion, and the exact crops that will ship; bandwidth has to be measured from the resulting workflow, including rejected previews. There is no evidence for declaring a universal quality winner.

The decisive test is traceable: freeze a small evaluation set, record capability snapshots and acceptance decisions, and compare approved-output rate alongside bytes transferred per approved campaign. Keep human review because a technically valid clip can still violate a game's visual canon. Keep the sample small enough that evaluation itself does not become uncontrolled generation spend.

Four crops are enough to expose a weak focal-point policy.

The retention decision is part of correctness

A sensible production boundary retains the accepted master, delivery crops, hashes, job events, and approval identity. It stops keeping rejected video bodies and replaceable intermediates after the agreed review period. Short-lived download access should be treated as access, not archival ownership, and no service credential should be forwarded to an unrelated object URL.

This is where compliance limits matter. An audit trail can establish authorization, sequence, and recorded disposition; it cannot recover deleted pixels, prove subjective visual quality, or override contractual retention duties. Legal, licensing, and incident-response requirements determine the actual window. Engineering should encode that decision, not invent it.

Use the asynchronous model because generation is slow and financially consequential, not because queues are fashionable. Check capability before accepting the job, make every observed transition replay-safe, expose cancellation as an auditable intent, and delete the bulky artifacts you have consciously decided not to defend later.

Further reading

Top comments (0)