Event-driven architecture and serverless

Objective 5.6 · DevOps Fundamentals · 10% of the exam

Why this matters

This objective closes the DevOps domain with the architectural style the cloud made practical. Event-driven design and serverless compute are not the same thing, but they fit together so naturally that CompTIA treats them in one scope bullet — exploring architectures for cloud applications.

The reason they matter here rather than in the architecture domain is operational. Event-driven systems are loosely coupled, which makes them resilient and independently scalable, and harder to observe, because no single component knows the whole story. That trade is the substance of this lesson, and it is also why objective 3.3's tracing exists.

The lesson

Events, producers and consumers, and the loose coupling that follows

An event is a record that something happened — an order was placed, a file was uploaded, a user registered, a resource was created. A producer emits it. A consumer reacts. The producer does not know who consumes, and does not wait for them.

Contrast that with a direct call. In a request-driven design, the ordering service calls the inventory service, the email service and the analytics service. It knows all three, waits for all three, and fails if any of them is down. Adding a fourth consumer means changing the ordering service.

In an event-driven design the ordering service publishes OrderPlaced. Whatever needs to react subscribes. Adding a consumer changes nothing upstream.

What that buys, and these are the exam's answers:

  • Loose coupling. Components are independently deployable and independently changeable.
  • Resilience. A consumer being down does not fail the producer; the event waits.
  • Independent scaling. Each consumer scales to its own load.
  • Extensibility. New behaviour is a new subscriber.

What it costs:

  • Eventual consistency. The order exists before the inventory reflects it. Systems that need immediate consistency fit badly.
  • Harder debugging. No single call stack spans the flow.
  • Harder reasoning. "What happens when an order is placed" is answered by finding every subscriber, and that is not visible in any one place.
  • Operational complexity, since there is now messaging infrastructure to run and monitor.

The useful distinction between two flavours: an event notification says something happened and carries an identifier, so consumers fetch details — small messages, more calls. Event-carried state transfer includes the data, so consumers need not call back — larger messages, looser coupling, and stale data risk. Both are legitimate and the trade is worth recognising.

Functions as a service: no server to size, and the trade that comes with it

Serverless means the provider runs the infrastructure entirely: you supply a function, the provider runs it when triggered, scales it automatically, and charges for execution rather than for uptime. There are servers; they are not yours to think about.

What it removes: instance sizing, patching, scaling configuration, capacity planning, and paying for idle. That is a substantial amount of the work from domains 2 and 3, which is the appeal.

What it adds, and this is the honest list:

  • Execution constraints — a maximum run duration, memory limits, limited local disk, restrictions on the runtime.
  • Cold starts, below.
  • Statelessness, enforced rather than encouraged.
  • Vendor lock-in, which is real: functions are written against a provider's event and runtime model, and the surrounding services are provider-specific. Objective 1.1's lock-in discussion applies strongly.
  • Debugging and testing are harder locally.

Where it fits well: event processing, scheduled jobs, API backends with variable load, glue between services, and — the pattern used repeatedly in this course — operational automation reacting to platform events (objective 5.1).

Where it fits badly: long-running processing that exceeds the duration limit, workloads needing consistent very low latency, anything requiring specialised runtimes or hardware, and steady high-volume workloads, where per-invocation pricing can cost more than a reserved instance.

The middle ground worth knowing: serverless containers, where you supply a container image and the platform runs it without you managing nodes. It keeps much of the operational benefit with fewer runtime constraints and less lock-in.

Cold starts, execution limits and statelessness as real design constraints

Three constraints that shape serverless design rather than merely annoying it.

Cold starts. When no warm instance of a function exists, the platform must provision one and initialise the runtime before the code runs. That adds latency — typically tens to hundreds of milliseconds, sometimes more for heavy runtimes or large dependency sets, and longer again when the function is attached to a private network.

It matters for user-facing latency and not much otherwise. Mitigations, in order of usefulness: keep the deployment package small and the dependency tree shallow; move initialisation outside the request handler so it is reused by warm instances; use provisioned concurrency where the platform offers it for latency-critical paths; and accept it for asynchronous work, where a few hundred milliseconds is irrelevant.

Execution limits. A maximum duration per invocation, a memory ceiling, a payload size limit, and limited temporary storage. The design response to a job that exceeds the duration is to decompose it — break it into steps coordinated by a workflow service or chained through a queue — or to move it to a container or instance. A scenario describing a long batch job failing part way through is usually describing a duration limit.

Statelessness. Each invocation may run on a fresh instance with nothing retained, so state belongs in a database, cache or object store. The practical subtlety is that the execution environment is sometimes reused, so variables outside the handler persist between invocations — which is useful for caching connections and dangerous if you accidentally carry one request's data into the next. Treat reuse as an optimisation you may benefit from and must never depend on.

Event sources, fan-out, and the ordering guarantee you may not have

Event sources in a cloud are plentiful, and recognising them is part of the objective: an object uploaded to storage, a message on a queue or topic, a database change stream, an HTTP request through an API gateway, a schedule, a platform event such as a resource being created or a security finding being raised, and an IoT or streaming source.

Fan-out is one event reaching many consumers, usually through a topic. It is what makes the extensibility above work, and the patterns worth knowing:

  • Topic fan-out — every subscriber gets a copy.
  • Queue per consumer behind a topic, so each consumer gets durability, retries and a dead-letter queue of its own. This is the robust pattern and the one to prefer.
  • Event bus with routing rules, so consumers receive only events matching a filter.

The guarantees you may not have, which is the heading's point and the most examinable part:

  • Ordering is usually not guaranteed. Events may arrive out of order, especially with fan-out and parallel consumers. If order matters, use an ordering-capable service, or include sequence numbers and have consumers handle reordering — and expect to trade throughput for it.
  • Delivery is at-least-once, so duplicates happen and idempotency is mandatory (objective 5.4).
  • Delivery is not guaranteed indefinitely. Retries expire; without a dead-letter queue, events are lost silently.
  • There is no global transaction. An event published after a database commit may fail to publish, leaving the two inconsistent. The transactional outbox pattern — write the event to the same database in the same transaction, then publish from there — is the standard remedy, and the saga pattern coordinates multi-step work with compensating actions instead of a distributed transaction.

Observability is the operational consequence: you cannot follow an event-driven flow without distributed tracing (objective 3.3), because no component sees the whole path. Propagating a correlation identifier through every event is the minimum, and it is worth designing in from the start rather than retrofitting.

Cost shape: paying per invocation, and the runaway loop that bills for itself

Serverless has a different cost shape, and the exam cares about both ends of it.

You pay per invocation and per unit of compute time — a function of memory allocated and duration. So:

  • Idle is free. Nothing running costs nothing, which is transformative for spiky or infrequent workloads.
  • Cost scales directly with usage, which makes it predictable per unit and unbounded in total.
  • Steady high volume can be more expensive than a reserved instance doing the same work. There is a crossover, and it is worth calculating rather than assuming.
  • Memory allocation affects both price and speed, since more memory usually means proportionally more CPU. A function given more memory can finish faster and sometimes cost less overall — a genuinely counter-intuitive optimisation.

The failure in the heading is the recursive invocation loop, and it is worth knowing by name because it is the classic serverless cost incident: a function triggered by writes to a storage bucket writes its output back to the same bucket, which triggers the function, which writes again. It scales automatically, so it runs as fast as the platform allows, and the bill grows until someone notices.

The guards, which are the same controls from objective 1.8 applied here:

  • Never write output to the same location that triggers the function, or use prefix and suffix filters so it cannot re-trigger.
  • Set concurrency limits per function. This is the hard cap; a budget alert is not.
  • Set sensible timeouts, so a stuck invocation does not run to the maximum.
  • Use budget alerts and anomaly detection, which is what catches the pattern quickly even though it does not stop it.
  • Watch the downstream too — a fan-out that multiplies invocations can also overwhelm a database or exhaust a third-party rate limit, which turns a cost incident into an outage.

What to take into the exam

  • Event-driven buys loose coupling, resilience, independent scaling and extensibility; it costs eventual consistency, harder debugging and messaging infrastructure.
  • Serverless removes sizing, patching and idle cost and adds cold starts, execution limits, enforced statelessness and lock-in.
  • Cold start mitigations: small packages, initialise outside the handler, provisioned concurrency. Environment reuse is an optimisation, never a dependency.
  • Ordering is usually not guaranteed; delivery is at-least-once — idempotency is mandatory, and a dead-letter queue prevents silent loss.
  • Queue-per-consumer behind a topic is the robust fan-out pattern.
  • Transactional outbox solves the publish-after-commit gap; saga replaces distributed transactions.
  • You cannot debug event-driven flows without tracing and correlation IDs.
  • Idle is free; volume is unbounded. Guard against the recursive invocation loop with trigger filters and concurrency limits — a budget alert detects, it does not prevent.

Practise what you just read

1. What does an event-driven design give up in exchange for loose coupling?

Select one

  1. The ability to scale consumers independently
  2. Resilience to a consumer being unavailable
  3. Immediate consistency across components
  4. The ability to add new consumers without modifying the component that produces the events being consumed
Show answer

C. The order exists before the inventory reflects it. Systems requiring a read to be immediately correct after a write fit badly, and debugging is harder because no component sees the whole flow.

2. What is a cold start in a serverless function?

Select one

  1. The period during which a function is unavailable because the platform is applying a runtime security update to it
  2. The first invocation after a deployment fails
  3. A function that has never been invoked
  4. Latency while the platform initialises a new instance
Show answer

D. The platform must provision and initialise a runtime before the code runs, adding latency. It matters for user-facing paths and is largely irrelevant for asynchronous work.

3. Which mitigation most directly addresses cold start latency on a critical path?

Select one

  1. Provisioned concurrency
  2. Increasing the function's timeout
  3. Splitting the function into several smaller functions so that each one initialises a smaller set of dependencies at startup
  4. Reducing the function's memory allocation
Show answer

A. Provisioned concurrency keeps initialised instances ready. Smaller packages and initialising outside the handler help too, and accepting it is correct wherever a few hundred milliseconds does not matter.

7 more questions on this objective are part of the full course.

Practise the full question bank in the exam simulator

Hands-on labs

All hands-on labs

This is an independent study companion for CompTIA Cloud+ CV0-004 and is not produced by or endorsed by CompTIA.