@hyperframes/gcp-cloud-run 0.6.79

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,179 @@
1
+ # HyperFrames distributed render orchestration on Cloud Workflows.
2
+ #
3
+ # Plan → BuildChunkList → AssertChunkCount → RenderChunks (parallel) → Assemble
4
+ #
5
+ # Mirrors the Step Functions state machine in
6
+ # `examples/aws-lambda/template.yaml`. Every step POSTs to the same Cloud Run
7
+ # service URL (passed in as `args.ServiceUrl`) and varies only the body's
8
+ # `Action`. The service returns the step's small result body on 2xx; on a
9
+ # non-retryable failure it returns HTTP 400, on a retryable failure HTTP 5xx —
10
+ # the `retryable` predicate below keys off exactly that split.
11
+ #
12
+ # The final returned object accumulates every step's result body so
13
+ # `getRenderProgress` can read frame totals + per-step durations on success:
14
+ # { Plan: {...}, Chunks: [{...}, ...], Assemble: {...} }
15
+ #
16
+ # Deploy with `gcloud workflows deploy` (the Terraform module / the
17
+ # `hyperframes cloudrun deploy` command do this for you).
18
+
19
+ main:
20
+ params: [args]
21
+ steps:
22
+ - init:
23
+ assign:
24
+ - serviceUrl: ${args.ServiceUrl}
25
+ - projectGcsUri: ${args.ProjectGcsUri}
26
+ - planOutputGcsPrefix: ${args.PlanOutputGcsPrefix}
27
+ - outputGcsUri: ${args.OutputGcsUri}
28
+ - config: ${args.Config}
29
+
30
+ # ── Plan (Activity A) ────────────────────────────────────────────────────
31
+ - plan:
32
+ try:
33
+ call: http.post
34
+ args:
35
+ url: ${serviceUrl}
36
+ timeout: 1800
37
+ auth:
38
+ type: OIDC
39
+ body:
40
+ Action: plan
41
+ ProjectGcsUri: ${projectGcsUri}
42
+ PlanOutputGcsPrefix: ${planOutputGcsPrefix}
43
+ Config: ${config}
44
+ result: planResp
45
+ retry:
46
+ predicate: ${retryable}
47
+ max_retries: 4
48
+ backoff:
49
+ initial_delay: 2
50
+ max_delay: 60
51
+ multiplier: 2
52
+ - capturePlan:
53
+ assign:
54
+ - planResult: ${planResp.body}
55
+ - chunkCount: ${planResult.ChunkCount}
56
+
57
+ # ── BuildChunkList + AssertChunkCount ──────────────────────────────────────
58
+ - assertChunkCount:
59
+ switch:
60
+ - condition: ${chunkCount > 0}
61
+ next: buildChunkList
62
+ next: planProducedZeroChunks
63
+ - planProducedZeroChunks:
64
+ raise:
65
+ code: PLAN_PRODUCED_ZERO_CHUNKS
66
+ message: "Plan returned ChunkCount=0 — the composition produced no frames. Non-retryable producer-side invariant violation."
67
+ - buildChunkList:
68
+ # Pre-size the ordered chunk-URI + per-chunk result lists so the
69
+ # parallel branches below assign by index (distinct indices, no
70
+ # read-modify-write race on a shared accumulator).
71
+ assign:
72
+ - chunkIndexes: []
73
+ - chunkUris: []
74
+ - chunkResults: []
75
+ - fillLists:
76
+ for:
77
+ value: i
78
+ range: [0, ${chunkCount - 1}]
79
+ steps:
80
+ - appendSlots:
81
+ assign:
82
+ - chunkIndexes: ${list.concat(chunkIndexes, i)}
83
+ - chunkUris: ${list.concat(chunkUris, "")}
84
+ - chunkResults: ${list.concat(chunkResults, "")}
85
+
86
+ # ── RenderChunks (Activity B, fanned out) ──────────────────────────────────
87
+ - renderChunks:
88
+ parallel:
89
+ shared: [chunkUris, chunkResults]
90
+ # Run up to chunkCount chunks at once, clamped to 20 — Cloud
91
+ # Workflows hard-caps concurrent branches/iterations per execution
92
+ # at 20 (https://cloud.google.com/workflows/quotas). Above that,
93
+ # iterations queue regardless of concurrency_limit, so a config with
94
+ # maxParallelChunks > 20 still renders correctly; the extra chunks
95
+ # just wait. All chunkCount iterations always run.
96
+ concurrency_limit: ${math.min(chunkCount, 20)}
97
+ for:
98
+ value: idx
99
+ in: ${chunkIndexes}
100
+ steps:
101
+ - renderOneChunk:
102
+ try:
103
+ call: http.post
104
+ args:
105
+ url: ${serviceUrl}
106
+ timeout: 1800
107
+ auth:
108
+ type: OIDC
109
+ body:
110
+ Action: renderChunk
111
+ ChunkIndex: ${idx}
112
+ PlanGcsUri: ${planResult.PlanGcsUri}
113
+ PlanHash: ${planResult.PlanHash}
114
+ ChunkOutputGcsPrefix: ${planOutputGcsPrefix}
115
+ Format: ${planResult.Format}
116
+ result: chunkResp
117
+ retry:
118
+ predicate: ${retryable}
119
+ max_retries: 4
120
+ backoff:
121
+ initial_delay: 2
122
+ max_delay: 60
123
+ multiplier: 2
124
+ - storeChunk:
125
+ assign:
126
+ - chunkUris[idx]: ${chunkResp.body.ChunkGcsUri}
127
+ - chunkResults[idx]: ${chunkResp.body}
128
+
129
+ # ── Assemble (Activity C) ──────────────────────────────────────────────────
130
+ - assemble:
131
+ try:
132
+ call: http.post
133
+ args:
134
+ url: ${serviceUrl}
135
+ timeout: 1800
136
+ auth:
137
+ type: OIDC
138
+ body:
139
+ Action: assemble
140
+ PlanGcsUri: ${planResult.PlanGcsUri}
141
+ ChunkGcsUris: ${chunkUris}
142
+ AudioGcsUri: ${planResult.AudioGcsUri}
143
+ OutputGcsUri: ${outputGcsUri}
144
+ Format: ${planResult.Format}
145
+ # Forward the caller's exact-CFR request (Config.cfr) to assemble.
146
+ # `"cfr" in config` guards the optional key; when unset this is
147
+ # false, which the handler reads as the default -c copy path.
148
+ Cfr: ${("cfr" in config) and config.cfr}
149
+ result: assembleResp
150
+ retry:
151
+ predicate: ${retryable}
152
+ max_retries: 4
153
+ backoff:
154
+ initial_delay: 2
155
+ max_delay: 60
156
+ multiplier: 2
157
+
158
+ - done:
159
+ return:
160
+ Plan: ${planResult}
161
+ Chunks: ${chunkResults}
162
+ Assemble: ${assembleResp.body}
163
+
164
+ # Retry predicate: retry transient/server failures (429 + 5xx), never the
165
+ # handler's non-retryable 400s (bad input, plan-hash mismatch, unsupported
166
+ # format, …). Connection / timeout errors carry no `.code`; retry those too.
167
+ retryable:
168
+ params: [e]
169
+ steps:
170
+ - classify:
171
+ switch:
172
+ - condition: ${not("code" in e)}
173
+ return: true
174
+ - condition: ${e.code == 429}
175
+ return: true
176
+ - condition: ${e.code >= 500 and e.code < 600}
177
+ return: true
178
+ - nonRetryable:
179
+ return: false