Back to projects
The dashboard in action: placing an order and following the lot to shipment

Pizza Factory Simulator

Industrial Engineering and production planning on a seven-station pizza line: due-date estimation, lot handling and recipe adjustment, with live KPIs

A seven-station pizza line simulated on real message brokers: RabbitMQ carries dispatch commands and Kafka records what happens at each station. A scheduler picks the next job by dispatch rule, and a KPI service computes throughput, WIP, cycle time and the bottleneck in real time, compares them with Industrial Engineering theory, and streams everything to a 3D dashboard.

Role
Personal project (Requirements Analysis, System Design, Development, Testing)
Tech
PythonKafkaRabbitMQFastAPI + SSEThree.jsDockerIndustrial EngineeringProduction Control & Planning
01.

Background

Kafka and message queues both move messages, but from documentation alone the difference between them easily stays abstract: which messages need to be acknowledged as done, and which should be recorded for anyone to replay?

This project gives each one a real job on the same production line, and doubles as practice in Industrial Engineering and production scheduling: the oven is deliberately the slowest station, to see whether the measured numbers match theory.

02.

Key Features

Everything is done from one dashboard, and every change shows up on the line and in the KPIs right away.

Place orders
Place orders by hand with a priority, or let a generator place them at a set arrival rate
Adjust station recipes
Change each station's recipe per product and watch the bottleneck and KPIs respond
Live ETA
Each order's completion time and due date are estimated from bottleneck capacity and queued WIP
KPI trend charts
Throughput, WIP and cycle time over time, to compare before and after a change
Lot handling
Track each lot's current station and status, and handle rework caused by breakdowns
Station performance
Utilization and activity for every station at a glance, with the current bottleneck highlighted
03.

Commands vs Facts

The system follows one rule: RabbitMQ carries commands, Kafka carries facts. A command has one receiver that must report back when done. A fact only states what already happened, so anyone can read it and replay it.

RabbitMQ

Commands (please do this)

  • >Orders enter workorders.intake, then the scheduler dispatches jobs to each station's station.<id>.jobs queue
  • >Each station uses prefetch 1, handling one job at a time and acking only when done
  • >Failures go to a retry queue with a 5-second TTL; jobs over the retry limit or with an invalid format go to a DLQ

Kafka

Facts (this happened)

  • >Stations write op_started / op_completed to station.events, partitioned by station
  • >Order lifecycle events go to order.lifecycle, and KPI snapshots to a compacted topic
  • >The scheduler, KPI service and dashboard each read the same events with their own consumer group
04.

Life of an Order

  1. 01

    Order

    A generator places orders at a set arrival rate, or orders are placed by hand on the dashboard, and sent to RabbitMQ.

  2. 02

    Release

    The scheduler records a created event, estimates the ETA and due date, and releases orders by the active dispatch rule.

  3. 03

    Dispatch

    The scheduler sends the job to the next station's queue. The seven stations are dough, sauce, cheese, toppings, bake, slice and package.

  4. 04

    Process

    The station writes op_started, waits the real cycle time for that product, writes op_completed, then acks.

  5. 05

    Hand-off

    Stations never talk to each other. The scheduler reads the completion event from Kafka and dispatches the next step.

  6. 06

    Monitor

    The KPI service folds events into live metrics and pushes them to the 3D dashboard over SSE.

05.

Reliability

Stations and the scheduler can be interrupted at any moment. The goal is that no work is lost and none is counted twice.

Kafka first, then ack
A station acks only after the completion event is flushed to Kafka. A crash causes redelivery, and event_id dedupe records the work once
Retry and DLQ
Temporary failures are re-dispatched through a TTL retry queue; invalid messages go straight to the DLQ instead of retrying forever
Breakdowns and rework
Stations can break down and be repaired at random. A failed lot waits in a cool-down queue before returning to the scheduler, so it never blocks the station
Restart recovery
On restart the scheduler replays events from Kafka to rebuild WIP and lots in cool-down
Chaos testing
A script kills containers at random during a run to check that the line recovers by itself
06.

Scheduling and KPIs

Three pizza products take different times at each station, so dispatch rules produce real differences. The oven takes 12 to 14 seconds per pizza while other stations take 3 to 6, making it the bottleneck by design.

Dispatch rules
FIFO, EDD and SPT can be switched live, and the same set of orders can be compared across rules
Priority and due dates
Orders carry a priority from 1 to 99; at release the ETA is estimated from the bottleneck and queued WIP, and a due date is derived from it
Live KPIs
Throughput, WIP, cycle time (mean, p50, p95) and utilization per station
Bottleneck detection
Utilization with hysteresis picks the bottleneck station, so it does not flip back and forth between two stations
Checks against theory
With warm-up excluded, measurements are checked against Little's Law (L = λW) and bottleneck capacity
07.

Outcome

  • >In an overload test, measured throughput reached 91% of the theoretical bottleneck capacity, inside the expected 75% to 115% band.
  • >About 1,200 Python tests and 250 frontend tests, with integration tests running against real Kafka and RabbitMQ brokers.
  • >Docker starts the brokers, all seven stations and every service with one command, and the same seed reproduces the same orders.