← All work

IDEA Technology · R&D

VendAD

Digital signage CMS for vending machines

Role
End-to-end owner
Links
Stack
  • ASP.NET Core 8
  • EF Core
  • PostgreSQL
  • SQL Server
  • Redis
  • RabbitMQ
  • SignalR
  • MediatR
  • Flutter · Android TV
  • Docker
  • GitHub Actions
  • k6

Overview

A vending-machine advertising platform I owned end to end: the ASP.NET Core MVC portal, the backend and database, a Flutter player for Android TV, and the deployment that keeps a fleet of screens in sync.

What I built

System design

Event-driven cache invalidation

Approving or editing content publishes a RabbitMQ message. A background worker consumes it and invalidates only the affected Redis keys, so every screen picks up the change in seconds without polling.

Redis distributed lock with Lua

SETNX plus a Lua script serialises concurrent content updates and cache rebuilds across app instances, with a lease TTL so a crashed worker can never deadlock the fleet.

One codebase, two databases

EF Core with provider-agnostic migrations lets the same system run on PostgreSQL or SQL Server by configuration, verified with Testcontainers for both providers in CI.

Video validation pipeline

Uploads pass an ffprobe header check for a fast reject, then a full FFmpeg decode against codec, resolution, bitrate and duration policies before they can reach a screen.

Problems solved

  1. ProblemScreens in different locations kept showing stale content for up to 10 minutes after an admin update.

    SolutionContent changes now emit events that a background worker turns into targeted Redis invalidations. Propagation dropped from about 10 minutes to under 3 seconds.

  2. ProblemConcurrent admin edits left the cache inconsistent, so two screens could play different versions of the same campaign.

    SolutionCritical writes are serialised with a Lua-based Redis lock that has a lease and auto-expiry, plus a retry queue for failed acquisitions.

  3. ProblemVideos with unsupported codecs reached the players and failed on air.

    SolutionA two-stage check: ffprobe for an instant reject, a full FFmpeg decode for safety, and error messages that tell people exactly how to re-encode.

Gallery

Lessons