Skip to content

feat(backend): serve gRPC ItemService with event streaming - #161

Merged
vovinacci merged 2 commits into
mainfrom
feat/backend-grpc
Jul 21, 2026
Merged

feat(backend): serve gRPC ItemService with event streaming#161
vovinacci merged 2 commits into
mainfrom
feat/backend-grpc

Conversation

@vovinacci

@vovinacci vovinacci commented Jul 21, 2026

Copy link
Copy Markdown
Owner

grpc.aio server on :50051 in the same process as FastAPI (ADR-0002: backend serves, never dials): ListItems snapshot, GetItemStats, and WatchItemEvents backed by an in-process emit-after-commit broadcaster. Per-subscriber bounded queues; a slow consumer is disconnected rather than blocking the request path, and reconnect recovers state via the ListItems snapshot -- at-most-once transport eventing, the documented gap the NATS capstone later closes. gRPC Health Checking Protocol served; container healthcheck now verifies HTTP and gRPC together. RED metrics via server interceptor on the existing registry.

Codegen is generate-in-build through a single grpc_tools.protoc run (python + pyi + grpc stubs); buf.gen.yaml removed -- its protoc_builtin plugins require a host protoc binary, which clean CI and Docker builds do not have (masked locally by a Homebrew protoc). Generated code is never committed (gitignored, dockerignored, CI gate). Serving is pinned to a single uvicorn worker: with multiple workers the broadcaster and gRPC listener fork per process behind SO_REUSEPORT and stream subscribers silently miss most events.

Summary by CodeRabbit

  • New Features

    • Added a gRPC API on port 50051 for listing items, retrieving statistics and streaming item creation/deletion events.
    • Added gRPC health reporting and monitoring metrics for requests and streamed messages.
    • Backend containers now validate both HTTP and gRPC health.
  • Documentation

    • Added guidance for exploring the gRPC API, generated stubs and contract compatibility checks.
  • Build & Quality

    • Builds and tests now generate required gRPC support automatically and prevent generated files from being committed.

grpc.aio server on :50051 in the same process as FastAPI (ADR-0002:
backend serves, never dials): ListItems snapshot, GetItemStats, and
WatchItemEvents backed by an in-process emit-after-commit broadcaster.
Per-subscriber bounded queues; a slow consumer is disconnected rather
than blocking the request path, and reconnect recovers state via the
ListItems snapshot -- at-most-once transport eventing, the documented
gap the NATS capstone later closes. gRPC Health Checking Protocol
served; container healthcheck now verifies HTTP and gRPC together.
RED metrics via server interceptor on the existing registry.

Codegen is generate-in-build through a single grpc_tools.protoc run
(python + pyi + grpc stubs); buf.gen.yaml removed -- its
protoc_builtin plugins require a host protoc binary, which clean CI
and Docker builds do not have (masked locally by a Homebrew protoc).
Generated code is never committed (gitignored, dockerignored, CI
gate). Serving is pinned to a single uvicorn worker: with multiple
workers the broadcaster and gRPC listener fork per process behind
SO_REUSEPORT and stream subscribers silently miss most events.
@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown

The latest Buf updates on your PR. Results from workflow Proto / lint-proto (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed✅ passed✅ passed⏩ skippedJul 21, 2026, 6:10 PM

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown

The latest Buf updates on your PR. Results from workflow Proto / breaking-proto (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed⏩ skipped⏩ skipped✅ passedJul 21, 2026, 6:10 PM

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@vovinacci, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 34 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c07c0af8-4dc5-4d36-aaa4-1f1276911395

📥 Commits

Reviewing files that changed from the base of the PR and between b298a41 and bb33821.

📒 Files selected for processing (7)
  • .github/workflows/images.yml
  • .github/workflows/proto.yml
  • Makefile
  • docs/ci.md
  • services/backend/README.md
  • services/backend/app/crud.py
  • services/backend/app/healthcheck.py

Walkthrough

The backend now generates protobuf/gRPC stubs during development, CI, and Docker builds. It adds an async gRPC ItemService with health checks, event streaming, metrics, lifecycle integration, container support, and corresponding tests and documentation.

Changes

gRPC backend integration

Layer / File(s) Summary
Code generation and image build
.github/workflows/images.yml, .gitignore, Makefile, services/backend/..., docs/prerequisites.md, docs/running-tests.md
Adds make generate, generated-code exclusions, gRPC dependencies, named protobuf build contexts, and Docker builder-stage stub generation.
ItemService and event streaming
services/backend/app/crud.py, services/backend/app/events.py, services/backend/app/grpc_server.py, services/backend/app/metrics.py, services/backend/tests/*
Adds database-backed unary RPCs, committed item events, bounded subscriber queues, WatchItemEvents, health registration, RED metrics, and async coverage.
Application lifecycle and health integration
services/backend/app/main.py, services/backend/app/healthcheck.py, services/backend/Dockerfile, deploy/compose/docker-compose.yml
Starts and stops gRPC with FastAPI, exposes port 50051, uses a combined HTTP/gRPC healthcheck, and runs one worker.
CI gates and service documentation
.github/workflows/backend.yml, AGENTS.md, README.md, docs/*, services/backend/README.md
Documents generation and gRPC operation, adds the no-committed-codegen CI gate, and introduces a gRPC contract exercise.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ItemServiceServicer
  participant broadcaster
  participant CRUD
  participant Database
  Client->>ItemServiceServicer: WatchItemEvents
  ItemServiceServicer->>broadcaster: subscribe
  Client->>CRUD: create or delete item
  CRUD->>Database: commit item change
  CRUD->>broadcaster: publish ItemEventPayload
  broadcaster->>ItemServiceServicer: deliver queued event
  ItemServiceServicer-->>Client: stream ItemEvent
Loading

Poem

A rabbit hops through protobuf snow,
While gRPC streams begin to flow.
Stubs bloom fresh in builder air,
Health checks watch with careful care.
CI guards the generated glow.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.95% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarises the main change: the backend now serves the gRPC ItemService with event streaming.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/backend-grpc

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (3)
docs/ci.md (1)

75-80: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Adjust terminology for accuracy regarding git ls-files.

Since git ls-files only checks the current git index (files that are currently tracked) rather than searching through the entire commit history, the phrase "was ever committed" is technically inaccurate. Consider updating it to "is currently tracked" or "is committed" to better reflect what the gate actually evaluates.

📝 Proposed wording update
 - **No committed generated code (backend):** a `git ls-files
   'services/backend/app/proto_gen/*'` check fails the pipeline if
-  anything under the generate-in-build gRPC stub directory was ever
-  committed (RFC-0001 D8, ADR-0002 -- Hard rule 1). `make generate` then
+  anything under the generate-in-build gRPC stub directory is
+  tracked (RFC-0001 D8, ADR-0002 -- Hard rule 1). `make generate` then
   runs before tests, since the backend imports the generated stubs at
   module load time.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/ci.md` around lines 75 - 80, Update the “No committed generated code
(backend)” documentation to replace “was ever committed” with wording that
accurately describes the git ls-files check, such as “is currently tracked” or
“is committed.” Preserve the existing explanation of the generate-in-build
directory and pipeline behavior.
Makefile (1)

104-113: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Regenerate into a clean proto_gen directory.

mkdir -p doesn't clear previously generated files, so stale modules from a prior make generate run could linger if the proto surface changes (e.g. a renamed/removed message or service). Cheap to close off now before more .proto files are added.

♻️ Proposed fix
 generate: ## Generate gRPC/protobuf Python stubs into services/backend/app/proto_gen (never committed -- RFC-0001 D8, ADR-0002)
 	$(PRINT_TARGET)
-	mkdir -p services/backend/app/proto_gen
+	rm -rf services/backend/app/proto_gen
+	mkdir -p services/backend/app/proto_gen
 	python -m grpc_tools.protoc -I proto \
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Makefile` around lines 104 - 113, Update the generate target to remove the
existing services/backend/app/proto_gen directory before recreating it, then
keep the protoc generation commands unchanged so each run starts from a clean
output directory.
services/backend/app/main.py (1)

28-41: 🩺 Stability & Availability | 🔵 Trivial

Startup coupling is intentional but worth keeping in mind operationally.

A gRPC bind/start failure now prevents the HTTP interface from starting at all, since both live in the same lifespan. This matches the PR's single-worker/consistent-state design and the combined health check, so no change requested — just flagging for awareness if HTTP-only availability is ever desired during a gRPC-specific outage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@services/backend/app/main.py` around lines 28 - 41, No code changes are
requested. Preserve the intentional coupling in lifespan: start gRPC before
yielding from lifespan and keep HTTP startup blocked if start_grpc_server fails,
while retaining graceful shutdown through grpc_server.stop(grace=5).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/images.yml:
- Line 41: Update the actions/checkout step to set persist-credentials to false,
preventing the GitHub token from being stored in local git configuration while
preserving the existing pinned action reference.

In `@services/backend/app/crud.py`:
- Around line 37-52: Update delete_item to perform the existence check and
deletion atomically using the delete statement’s RETURNING result, rather than
calling db.get first. Derive the deleted item’s name from the returned row,
return False without publishing when no row is returned, and preserve the
existing commit, rollback, and ItemEvent publishing behavior for successful
deletion.
- Around line 22-34: Update create_item so it calls db.flush() before
db.commit() to populate the persisted item attributes needed by
ItemEventPayload, then remove the post-commit db.refresh(item) dependency.
Publish the event immediately after a successful commit using the flushed
item.id and item.name, while retaining rollback handling for commit/flush
IntegrityError failures.

In `@services/backend/app/healthcheck.py`:
- Around line 18-29: Reduce the per-check timeouts in main() for both
urllib.request.urlopen and stub.Check so their sequential worst-case duration
remains safely below the Docker healthcheck’s 10-second limit. Preserve the
existing failure handling and SERVING-status validation.

In `@services/backend/README.md`:
- Around line 114-120: Update the activation command in the documented setup
sequence to reference the backend virtual environment from the repository root,
consistent with the path created by make venv-install. Keep the remaining
root-level commands unchanged.

---

Nitpick comments:
In `@docs/ci.md`:
- Around line 75-80: Update the “No committed generated code (backend)”
documentation to replace “was ever committed” with wording that accurately
describes the git ls-files check, such as “is currently tracked” or “is
committed.” Preserve the existing explanation of the generate-in-build directory
and pipeline behavior.

In `@Makefile`:
- Around line 104-113: Update the generate target to remove the existing
services/backend/app/proto_gen directory before recreating it, then keep the
protoc generation commands unchanged so each run starts from a clean output
directory.

In `@services/backend/app/main.py`:
- Around line 28-41: No code changes are requested. Preserve the intentional
coupling in lifespan: start gRPC before yielding from lifespan and keep HTTP
startup blocked if start_grpc_server fails, while retaining graceful shutdown
through grpc_server.stop(grace=5).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 62241cfe-750e-4eb0-89bb-37ed5361a2ed

📥 Commits

Reviewing files that changed from the base of the PR and between ebd57c1 and b298a41.

📒 Files selected for processing (26)
  • .github/workflows/backend.yml
  • .github/workflows/images.yml
  • .gitignore
  • AGENTS.md
  • Makefile
  • README.md
  • deploy/compose/docker-compose.yml
  • docs/architecture.md
  • docs/ci.md
  • docs/exercises/02-grpc-contract.md
  • docs/prerequisites.md
  • docs/running-tests.md
  • proto/buf.gen.yaml
  • services/backend/.dockerignore
  • services/backend/.ruff.toml
  • services/backend/Dockerfile
  • services/backend/README.md
  • services/backend/app/crud.py
  • services/backend/app/events.py
  • services/backend/app/grpc_server.py
  • services/backend/app/healthcheck.py
  • services/backend/app/main.py
  • services/backend/app/metrics.py
  • services/backend/pyproject.toml
  • services/backend/tests/test_events.py
  • services/backend/tests/test_grpc.py
💤 Files with no reviewable changes (1)
  • proto/buf.gen.yaml

Comment thread .github/workflows/images.yml
Comment thread services/backend/app/crud.py
Comment thread services/backend/app/crud.py
Comment thread services/backend/app/healthcheck.py
Comment thread services/backend/README.md
@vovinacci
vovinacci merged commit 58fbd53 into main Jul 21, 2026
11 checks passed
@vovinacci
vovinacci deleted the feat/backend-grpc branch July 21, 2026 18:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant