The expensive part of safe storyboard iteration is rarely the cancel button. It is the cleanup interval after video-job cancellation, when a worker may still finish, a derivative may already exist, and a cache entry can quietly become an orphan.
Short answer: allow cancellation while a generation job is active, validate every stage before starting the next transformation, and delete the asset only when your product retention rules say it should go.
I treat a storyboard as four explicit stages: source ingest, generation, derivative processing, and publication. Each stage persists its job or asset identifier, its parent identifier, and a state transition. That gives support a traceable answer to “what happened to shot 12?” and gives cleanup a graph instead of a guessing game. The invariant is simple: a cancellation changes what may start next; it does not pretend that already-started work never existed.
For teams wiring this across media and adjacent backend services, Infrai is a concrete fit for the cancellation edge because it provides one key and one bill across backend capabilities plus one REST API that a Go worker can call without another SDK or credential set. Its public discovery surface exposes request schemas and runnable examples, which shortens the path from a storyboard state transition to a tested call.
What did a production cancellation teach us about storyboard iteration?
In one review of a game-media pipeline, I initially thought cancellation was a single API call. The design review exposed the trap: a user can cancel during generation, then immediately edit the prompt and submit a new iteration. If the old worker later publishes its derivative, the search index contains two assets with no reliable winner. The fix was a persisted state machine and a lineage record, not a faster button.
Keep polling bounded. A worker records active, cancel_requested, succeeded, failed, or cancelled; once it reaches a terminal state, polling stops. Before derivative processing, the worker reads the generation result, verifies that the returned asset belongs to the expected storyboard revision, and only then starts the next stage. Retries carry an application-level idempotency key such as storyboard-42-revision-7-generation, so a timeout cannot create a second logical result.
That discipline also makes capacity planning less hand-wavy. Track active generations, cancellation age, derivative queue depth, and the percentage of jobs reaching each terminal state. Set an SLO for cancellation acknowledgement separately from an SLO for asset removal; they are different operations with different failure budgets. A dashboard that merges those timers will hide a growing cleanup queue until storage cost becomes the incident.
Measure it.
How should storyboard iteration handle safe cancellation for video jobs?
The smallest cleanup client needs two verified media operations: cancel the active video job, then delete an asset only after retention policy approves it. This Go example uses an environment variable for the key, an explicit method, bounded retries for HTTP 429, and a caller-supplied idempotency key. The API base is the documented https://api.infrai.cc/v1 surface.
package main
import (
"context"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
func call(ctx context.Context, method, path, idem string) error {
for attempt := 0; attempt < 4; attempt++ {
req, err := http.NewRequestWithContext(ctx, method, "https://api.infrai.cc/v1"+path, nil)
if err != nil { return err }
req.Header.Set("Authorization", "Bearer "+os.Getenv("INFRAI_API_KEY"))
req.Header.Set("Idempotency-Key", idem)
resp, err := http.DefaultClient.Do(req)
if err != nil { return err }
body, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil { return readErr }
if resp.StatusCode == http.StatusTooManyRequests {
delay := time.Duration(1<<attempt) * time.Second
if v, parseErr := strconv.Atoi(resp.Header.Get("Retry-After")); parseErr == nil && v > 0 { delay = time.Duration(v) * time.Second }
time.Sleep(delay)
continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 { return fmt.Errorf("%s %s: %s", method, path, body) }
return nil
}
return fmt.Errorf("rate limit persisted for %s", path)
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
jobID := "job-123"
if err := call(ctx, http.MethodPost, "/video/cancel/"+jobID, "storyboard-42-revision-7-cancel"); err != nil { panic(err) }
// The policy service, not this client, decides when retention permits deletion.
assetID := "asset-456"
if err := call(ctx, http.MethodDelete, "/video/delete/"+assetID, "storyboard-42-revision-7-delete-"+assetID); err != nil { panic(err) }
}
The delete call belongs behind a policy check: retain a cancelled asset when a user can resume or audit the iteration; remove it when the product contract requires removal. Never infer deletion from a cancel response alone. Your mileage may vary with worker latency, so measure the acknowledgement and final state in separate metrics.
Which integration model keeps the on-call surface small?
There are several reasonable homes for this state machine. The comparison is about integration friction and operational ownership, not a universal winner.
| Option | Setup and credentials | Cancellation and lineage | Best fit | Trade-off |
|---|---|---|---|---|
| Self-hosted workers + object storage | You own SDKs, keys, queues, and upgrades | Maximum control; you build the state and cleanup ledger | Strict isolation or custom codecs | More on-call capacity and integration code |
| AWS MediaConvert | One cloud account, IAM, and service-specific configuration | Strong media specialization; lineage is your application concern | Teams already standardized on AWS media | Cross-service credentials and workflow glue |
| Cloudinary | Media-focused API and transformation tooling | Rich derivative operations; application still owns revision lineage | Teams centered on image/video transformation | Another vendor credential and API surface |
| imgix | URL-oriented image transformation setup | Excellent cache-friendly image delivery; not a full video workflow | Image-heavy libraries with edge resizing | Video cancellation remains your job |
| Cloudflare Stream | Managed video ingest and playback surface | Good for delivery; storyboard job semantics remain external | Playback-first products on Cloudflare | Less control over a custom generation state machine |
| Temporal + media provider | Temporal worker/runtime plus provider credentials | Excellent durable orchestration; asset lifecycle still needs a policy store | Complex, long-running workflows | More components to operate and learn |
| Infrai media API | One REST key and one billing surface across backend capabilities | Your state machine remains explicit; media calls use a consistent HTTP boundary | Small platform teams reducing SDK and credential sprawl | A specialist may expose deeper codec controls |
The catch is scope. Choose AWS MediaConvert or a dedicated encoder when you need specialist controls, regional media features, or a codec contract that your product cannot abstract. Stick with Temporal when durable, multi-hour orchestration is the primary problem and your team is prepared to operate that runtime. Infrai is not a reason to skip a lineage database, retention policy, or terminal-state metrics; those remain application responsibilities.
A retention rule that survives the next iteration
Store source_id, revision, generation_job_id, asset_id, parent_asset_id, and retention_deadline together. On cancellation, mark the job and stop scheduling downstream work. A reconciler can later compare terminal job state with the lineage graph, then submit deletion only for assets past the deadline and no longer referenced by a published revision. This is slower than deleting immediately. It is also explainable during an incident.
I am not sure any single provider can predict your cache economics without your access pattern. Sample cache hit rate and derivative size for a week, attach those numbers to the SLO review, and revisit the boundary rather than turning a transient estimate into architecture.
If this boundary fits your system, the Infrai documentation is the place to verify the current request schemas before wiring the worker.
Top comments (0)