Purpose

Defines the required behavior of the companion runnable example (external repo: kafka-backed-local-read-replica-article) that backs the local-cache-via-kafka article — the readiness-gating library, the Kafka-consuming app, and its docker-compose deployment.

Requirements

Requirement: Readiness-gating library module

The example repo SHALL contain a standalone library module providing an annotation to mark Kafka listener methods as config consumers, tracking per-listener last-consumed-time and error state, and exposing a “fully consumed” state (no new messages for a configurable N seconds AND no consume errors) integrated with Spring Boot’s readiness state.

Scenario: Readiness stays DOWN until fully consumed

Scenario: Readiness flips UP after quiet period

Requirement: Local cache consumption from compacted topic

The main app module SHALL consume the compacted topic into an in-memory, thread-safe local cache using the readiness-gating library, and SHALL treat a record with a null value as a tombstone that removes (or marks inactive) the corresponding cache entry.

Scenario: Cache populated from topic

Scenario: Tombstone removes entry

Requirement: Cache-backed scheduled job and endpoint

The app module SHALL expose one @Scheduled job and one HTTP endpoint, both reading from the local cache, demonstrating consumption of cached data after readiness.

Scenario: Endpoint reflects cache contents

Requirement: Per-container consumer group via env prefix + Docker hostname suffix

Each app container SHALL run with a distinct Kafka consumer group name, assembled from a human-picked prefix (CONSUMER_GROUP_PREFIX, set in docker-compose.yml) and Docker’s own per-container HOSTNAME value, joined via Spring Boot property placeholder resolution — no explicit per-instance env var, sidecar, or generated UUID required.

Scenario: Distinct consumer groups across containers

Requirement: KRaft-mode Kafka with UI

The docker-compose stack SHALL run Kafka in KRaft mode (no ZooKeeper) alongside a kafka-ui service reachable from the reader’s browser on a published port, for inspecting topics and consumer groups.

Scenario: Kafka UI shows topic and consumer groups

Requirement: Compacted topic pre-populated outside the app

The compacted topic SHALL be created with cleanup.policy=compact and pre-populated with sample data by a one-shot kafka-topic-init compose service using the Kafka CLI, which the app service depends on via condition: service_completed_successfully — not by application code.

Scenario: Topic exists and has data before app starts consuming

Requirement: One-command docker-compose deployment

The example repo SHALL provide a Makefile target (or documented equivalent) that builds the app image (transitively building the readiness-gate library) and brings up the full stack — Kafka, kafka-ui, topic-init, app — with a single docker compose invocation, with the app’s HTTP port published to the host. The app module SHALL carry no dependency (database, HTTP client, API-doc generator, etc.) not required to demonstrate the Kafka-backed local cache pattern.

Scenario: Clean clone reaches a running demo

Requirement: README quick start and observability instructions

The README SHALL document: the one-command docker compose deploy path, how the env-prefix-plus-Docker-hostname consumer group is assembled, how to curl the cache-backed endpoint directly on the host, how to observe consumer groups and the compacted topic in kafka-ui, how tombstone deletes behave, and how to scale the app service to observe additional distinct consumer groups.

Scenario: README covers all required topics