Matías Fernández / System Design
ESEVENT-DRIVEN · FIELD NOTE 03

AZURE · REALTIME · ASYNCHRONOUS WORK

Event-driven,
but grounded in durable truth.

This is how we implemented a website RAG platform where chat had to feel immediate while ingestion could take minutes. The answer was not turning everything into an event: it was choosing two asynchronous channels and keeping one clear authority for state.

PUBLISHED
SEP 2 2026
READING
~12 min
PLATFORM
AZURE
CASE
RAG + CHATBOT

01 / THE ARCHITECTURE DECISION

Two event planes.
Two kinds of latency.

Conversation needs low-latency responses over persistent connections. Crawling needs buffering, retries, and independent scaling. One mechanism would have mixed incompatible guarantees.

REALTIME

Azure Web PubSub

Widget ↔ API

Chat messages and update signals delivered with low latency.

DURABLE WORK

Queue Storage + Functions

API → worker

Crawls decoupled from the request, with retries and independent processing.

AUTHORITY

Azure SQL + REST

API → clients

Queryable state that can be reconciled after a disconnect or missed event.

DESIGN RULE

We did not start with “which event service should we use?” We started with “does this message notify, schedule work, or represent durable truth?”

02 / CONVERSATION PLANE

Web PubSub moves the answer.
The API keeps control.

The widget communicates over WebSocket, but it never executes business logic or reaches models and data directly. Web PubSub manages connections; the API validates context, orchestrates Azure OpenAI, persists the message, and returns a correlated response.

  1. 01
    NEGOTIATE

    The API resolves the visitor and issues a short-lived connection URL.

  2. 02
    CREATE MESSAGE

    The widget publishes an event with conversation, session, and message hash.

  3. 03
    ORCHESTRATE

    The webhook reaches FastAPI, which runs retrieval and Azure OpenAI.

  4. 04
    COMMIT

    The answer, sources, and cost are stored before delivery.

  5. 05
    CORRELATE

    The API publishes to the originating client; the hash associates and deduplicates.

03 / INGESTION PLANE

The request starts it.
The queue finishes the work.

A crawl launches a browser, discovers links, transforms content, generates embeddings, and writes to several stores. Holding the HTTP request open would have coupled the operator to runtime duration and every downstream dependency.

  1. 01
    API

    Creates GlobalCrawl and LocalCrawl as INPROGRESS.

  2. 02
    QUEUE

    Publishes the URL and correlation identifiers.

  3. 03
    FUNCTION

    Extracts, discovers, and reports through an internal callback.

  4. 04
    API

    Stores Markdown, generates metadata, embeddings, and vectors.

  5. 05
    STATE

    Recomputes status and publishes an admin notification.

Aggregate state is a function, not an event

INPROGRESS
When any active URL is still processing.
FAILED
When none is active and at least one failed.
COMPLETED
Only when every active URL completed.

04 / REAL GUARANTEES

Design for repetition.
Reconcile what you separate.

Queues and realtime channels improve decoupling and responsiveness, but introduce duplicates, delays, partial failures, and intermediate states. Reliability appears when those cases are part of the contract.

01

IDEMPOTENCY

URL normalization and existence checks reduce duplicates. A recrawl reuses the existing identity. Callbacks must tolerate repetition.

02

CORRELATION

Request ID, message hash, connection, conversation, global crawl, local crawl, and dequeue count join a distributed operation.

03

BOUNDED RETRIES

The Function retries transient failures; at the limit, it reports FAILED instead of leaving work pending forever.

04

RECONCILIATION

SQL is authoritative, but Blob and the vector index can drift. Metrics and repair jobs must detect that mismatch.

HONEST SEMANTICS

The system does not promise exactly-once. It accepts repeated delivery and makes effects safe under at-least-once processing.

05 / FAILURE MODES

The hard part starts
after the happy path.

The architecture earns its value in what it does when a channel, worker, or external write fails halfway through the flow.

A notification is missed

The admin refetches REST; the event accelerates the UI but does not define state.

The worker fails

The queue enables retries. State ends as FAILED when the budget is exhausted.

SQL, Blob, or vectors diverge

The system does not claim COMPLETED while a required artifact is missing; residual drift is reconciled.

An active crawl is deleted

Soft deletion does not magically cancel a running Function. Run IDs, cancellation state, and cooperative polling are required.

06 / THE PLAYBOOK

Six questions before
choosing the broker.

The product defines the guarantees; Azure implements the channel. This order prevents one tool from being used for problems that only look alike on a diagram.

  1. 01

    Does the message announce a fact or assign work?

  2. 02

    Which component owns the source of truth?

  3. 03

    What happens if the message arrives twice?

  4. 04

    Which identifier correlates the whole journey?

  5. 05

    When do retries stop?

  6. 06

    How is partial state detected and repaired?

Events decouple.
State explains what happened.

Azure Web PubSub made conversation immediate. Queue Storage and Functions isolated heavy work. Azure SQL kept a queryable history. The solution worked because every piece had a distinct responsibility and guarantees were designed around failure, not only flow.

07 / REFERENCES

Sources and context

The implementation is a sanitized synthesis of the architecture document supplied for this article. External references document the Azure services and patterns.

  1. 01Microsoft LearnEvent-driven architecture style
  2. 02Microsoft LearnAzure Web PubSub overview
  3. 03Microsoft LearnAzure Queue Storage trigger for Functions
  4. 04Microsoft LearnCloud design patterns