CRUMB a card from devarno-cloud

BuildJobMessage Wire Format

sparki beginner 3 min read

ELI5

A build job on the queue is a small JSON envelope: who triggered it, what project, which commit, and an integer priority. The broker also stamps a unique message ID on the outside for delivery tracking.

Technical Deep Dive

Defined in internal/mq/producer.go as BuildJobMessage. Marshaled with encoding/json and published to QueueBuilds with content-type application/json (set by the producer) and a UUID MessageId AMQP property assigned per attempt.

Field Layout

packet
0-71: "build_id (UUID string)"
72-143: "project_id (UUID string)"
144-183: "commit_sha (40-hex)"
184-247: "branch (variable)"
248-311: "triggered_by (variable)"
312-319: "priority (int)"
320-383: "timestamp (RFC3339)"
384-511: "metadata (object, optional)"

(Bit layout above is illustrative — the wire format is JSON; widths show typical byte ranges, not fixed offsets.)

JSON Schema (Effective)

FieldTypeRequiredNotes
build_idstring (UUID)yesEngine-generated, becomes the build row PK
project_idstring (UUID)yesFK into projects table
commit_shastringyes40-hex per build-result.json schema
branchstringyesGit ref short form
triggered_bystringyesUser ID, system actor, or webhook:github
priorityintyesAMQP message priority (0–9 typical)
timestampRFC3339 stringyesMarshaled from time.Time
metadataobjectnoFree-form; omitempty

Example

{
"build_id": "4c8c7a8e-2c3a-4f4f-9f88-7c9a8e8a6b2d",
"project_id": "11111111-2222-3333-4444-555555555555",
"commit_sha": "abcdef0123456789abcdef0123456789abcdef01",
"branch": "main",
"triggered_by": "user:alex",
"priority": 7,
"timestamp": "2026-05-05T09:30:00Z",
"metadata": {"source": "webhook:github"}
}

AMQP Envelope Properties

  • content-type: application/json
  • message_id: UUID generated per publish attempt (uuid.New().String() in publishWithRetry); persists across retries within the same attempt.
  • priority: copied from BuildJobMessage.Priority.
  • delivery_mode: persistent (set by the producer to survive broker restart).

Key Terms

  • commit_sha → 40-character lowercase hex; the canonical schema (build-result.json) enforces ^[a-f0-9]{40}$
  • triggered_by → free-form actor string; convention is <kind>:<id>
  • priority → AMQP integer priority; the broker honours it when the queue is declared with x-max-priority
  • metadata → escape hatch for non-canonical fields; consumers must treat unknown keys as opaque

Q&A

Q: Is metadata validated by the broker or consumer? A: Neither. It is map[string]interface{} with omitempty; consumers iterate keys they recognise and ignore the rest.

Q: What happens if commit_sha is shorter than 40 chars? A: The wire format does not enforce it (it is a Go string), but the downstream build-result.json schema does (^[a-f0-9]{40}$); a short SHA fails when the build row is later validated against the contract.

Q: Does the producer mint build_id or does the caller? A: The caller (REST handler) mints build_id before publishing, so the synchronous response can return the ID even if the publish later retries.

Examples

A GitHub webhook arrives, the engine assigns build_id, writes a pending build row, then calls Producer.PublishBuildJob. If RabbitMQ is unreachable for 4+ seconds (1+2+ext-backoff), the producer returns; the engine flips the row to failed with reason “queue unreachable” and surfaces it in the websocket hub stream.

neighbors on the map