The API
In one line: two things are notable about this API — the message size cap, which constrains the storage design, and the operation that is missing, which confirms the retention model.
Topic lifecycle
create(topic_ID, topic_name)
| Parameter | Description |
|---|---|
topic_ID | Uniquely identifies the topic |
topic_name | Contains the name of the topic |
Returns an acknowledgment on success or an error on failure.
delete_topic(topic_ID)
| Parameter | Description |
|---|---|
topic_ID | The ID of the topic which is to be deleted |
Publishing and reading
write(topic_ID, message)
| Parameter | Description |
|---|---|
message | The message to be written in the system |
Writes a message to the specified topic_ID. Messages have a maximum size of 1 MB. Returns an acknowledgment or error.
read(topic_ID)
| Parameter | Description |
|---|---|
topic_ID | The ID of the topic against which the message will be read |
Retrieves a message object from the specified topic_ID.
The 1 MB cap is a design constraint, not an arbitrary limit
The claim-check pattern. Asked how to publish a huge payload, the answer is that you do not — you publish where it is.
Interviewers ask about limits like this to see whether you treat them as trivia or as consequences.
A bounded, small message size is what makes the storage design in Lesson 8 viable. Messages are written as contiguous appends to a local log, and reads are served from page cache — both of which work well precisely because records are small and uniform. Multi-gigabyte messages would defeat the sequential-write assumption and blow out the cache.
It also shapes how the system gets used. Large payloads go to blob storage, and the pub-sub message carries a reference — a URL plus metadata. This is the standard "claim check" pattern, and it is the correct answer when someone asks how to publish a 500 MB video through the system: you don't; you publish where it is.
Subscription
subscribe(topic_ID)
| Parameter | Description |
|---|---|
topic_ID | The ID of the topic to which the consumer will be subscribed |
Registers the consumer as a subscriber to the specified topic_ID.
unsubscribe(topic_ID)
| Parameter | Description |
|---|---|
topic_ID | The ID of the topic against which the consumers will be unsubscribed |
Removes the consumer's subscription to the specified topic_ID.
Subscription is a persistent registration, not a live connection
subscribe writes a durable record — Lesson 4 has it going into a relational database, Lesson 5 into storage alongside consumer metadata. It is not a socket that dies when the consumer restarts.
That matters because it is what allows a consumer to go away and come back. Its subscription survives, its offset survives, and it resumes from where it stopped. A subscription tied to a live connection would lose both, and every reconnect would mean either replaying everything or skipping whatever arrived while it was gone.
This is the same distinction message-queue design drew about pull-based consumers: the durable state lives in the system, not in the connection.
What is missing
There is no delete_message call, and that is the point
Compare with the messaging queue chapter's API, where delete message was a required functional operation and the reason at-least-once delivery worked.
Here it is absent, and deliberately so. Deletion is automatic on retention expiry, driven by time rather than by any consumer's action. There is no call a subscriber can make to remove a message, because a message does not belong to any one subscriber — removing it would destroy other subscribers' data.
This single absence encodes the whole model:
- No consumer can destroy data → many readers can safely share one copy.
- Deletion is time-driven → storage is sized by retention, not by backlog.
- Reading is non-destructive → each consumer needs its own position, which is why offsets exist.
If you can only remember one thing about the pub-sub API versus the queue API, remember that delete moved from the consumer's control to the system's clock.
Key takeaway
Six calls: create and delete_topic for lifecycle, write and read for data, subscribe and unsubscribe for registration. The 1 MB cap enables the append-only storage design, and the absence of delete_message is what makes a shared single copy safe.
Interview signal by level
| Level | What a strong answer sounds like |
|---|---|
| L4 | "Publish, subscribe, and read, plus create and delete topics." |
| L5 | Notes the subscription is durable: "subscribe writes a record, so a consumer that restarts keeps its subscription and its position rather than starting over." |
| Staff+ | Reads the absence: "there's no delete-message call, unlike a queue — deletion is retention-driven, because a consumer deleting a message would destroy other subscribers' data. That's what makes a single shared copy safe and forces per-consumer offsets. And the 1 MB cap is what keeps writes as small contiguous appends; anything large goes to blob storage with a reference in the message." |
Next: the design that looks obvious and does not work.