Files
bakery-ia/DEMO_ARCHITECTURE.md
2025-10-03 14:09:34 +02:00

14 KiB
Raw Blame History

Demo Architecture - Production Demo System

Overview

This document describes the complete demo architecture for providing prospects with isolated, ephemeral demo sessions to explore the Bakery IA platform.

Key Features

  • Session Isolation: Each prospect gets their own isolated copy of demo data
  • Spanish Content: All demo data in Spanish for the Spanish market
  • Two Business Models: Individual bakery and central baker satellite
  • Automatic Cleanup: Sessions automatically expire after 30 minutes
  • Read-Mostly Access: Prospects can explore but critical operations are restricted
  • Production Ready: Scalable to 200+ concurrent demo sessions

Architecture Components

1. Demo Session Service

Location: services/demo_session/

Responsibilities:

  • Create isolated demo sessions
  • Manage session lifecycle (create, extend, destroy)
  • Clone base demo data to virtual tenants
  • Track session metrics and activity

Key Endpoints:

GET  /api/demo/accounts          # Get public demo account info
POST /api/demo/session/create    # Create new demo session
POST /api/demo/session/extend    # Extend session expiration
POST /api/demo/session/destroy   # Destroy session
GET  /api/demo/session/{id}      # Get session info
GET  /api/demo/stats              # Get usage statistics

2. Demo Data Seeding

Location: scripts/demo/

Scripts:

  • seed_demo_users.py - Creates demo user accounts
  • seed_demo_tenants.py - Creates base demo tenants (templates)
  • seed_demo_inventory.py - Populates inventory with Spanish data (25 ingredients per template)
  • clone_demo_tenant.py - Clones data from base template to virtual tenant (runs as K8s Job)

Demo Accounts:

Individual Bakery (Panadería San Pablo)

Email: demo.individual@panaderiasanpablo.com
Password: DemoSanPablo2024!
Business Model: Producción Local
Features: Production, Recipes, Inventory, Forecasting, POS, Sales

Central Baker Satellite (Panadería La Espiga)

Email: demo.central@panaderialaespiga.com
Password: DemoLaEspiga2024!
Business Model: Obrador Central + Punto de Venta
Features: Suppliers, Inventory, Orders, POS, Sales, Forecasting

3. Gateway Middleware

Location: gateway/app/middleware/demo_middleware.py

Responsibilities:

  • Intercept requests with demo session IDs
  • Inject virtual tenant ID
  • Enforce operation restrictions
  • Track session activity

Allowed Operations:

# Read - All allowed
GET, HEAD, OPTIONS: *

# Limited Write - Realistic testing
POST: /api/pos/sales, /api/orders, /api/inventory/adjustments
PUT: /api/pos/sales/*, /api/orders/*

# Blocked
DELETE: All (read-only for destructive operations)

4. Redis Cache Layer

Purpose: Store frequently accessed demo session data

Data Cached:

  • Session metadata
  • Inventory summaries
  • POS session data
  • Recent sales

TTL: 30 minutes (auto-cleanup)

5. Kubernetes Resources

Databases:

  • demo-session-db - Tracks session records

Services:

  • demo-session-service - Main demo service (2 replicas)

Jobs (Initialization):

  • demo-seed-users - Creates demo users
  • demo-seed-tenants - Creates demo tenant templates
  • demo-seed-inventory - Populates inventory data (25 ingredients per tenant)

Dynamic Jobs (Runtime):

  • demo-clone-{virtual_tenant_id} - Created per session to clone data from template

CronJob (Maintenance):

  • demo-session-cleanup - Runs hourly to cleanup expired sessions

RBAC:

  • demo-session-sa - ServiceAccount for demo-session-service
  • demo-session-job-creator - Role allowing job creation and pod management
  • demo-seed-role - Role for seed jobs to access databases

Data Flow

Session Creation

1. User clicks "Probar Demo" on website
   ↓
2. Frontend calls POST /api/demo/session/create
   {
     "demo_account_type": "individual_bakery"
   }
   ↓
3. Demo Session Service:
   - Generates unique session_id: "demo_abc123..."
   - Creates virtual_tenant_id: UUID
   - Stores session in database
   - Returns session_token (JWT)
   ↓
4. Kubernetes Job Cloning (background):
   - Demo service triggers K8s Job with clone script
   - Job container uses CLONE_JOB_IMAGE (inventory-service image)
   - Clones inventory data from base template tenant
   - Uses ORM models for safe data copying
   - Job runs with IfNotPresent pull policy (works in dev & prod)
   ↓
5. Frontend receives:
   {
     "session_id": "demo_abc123...",
     "virtual_tenant_id": "uuid-here",
     "expires_at": "2025-10-02T12:30:00Z",
     "session_token": "eyJ..."
   }
   ↓
6. Frontend stores session_token in cookie/localStorage
   All subsequent requests include:
   Header: X-Demo-Session-Id: demo_abc123...

Request Handling

1. Request arrives at Gateway
   ↓
2. Demo Middleware checks:
   - Is X-Demo-Session-Id present?
   - Is session still active?
   - Is operation allowed?
   ↓
3. If valid:
   - Injects X-Tenant-Id: {virtual_tenant_id}
   - Routes to appropriate service
   ↓
4. Service processes request:
   - Reads/writes data for virtual tenant
   - No knowledge of demo vs. real tenant
   ↓
5. Response returned to user

Session Cleanup

Every hour (CronJob):

1. Demo Cleanup Service queries:
   SELECT * FROM demo_sessions
   WHERE status = 'active'
   AND expires_at < NOW()
   ↓
2. For each expired session:
   - Mark as 'expired'
   - Delete all virtual tenant data
   - Delete Redis keys
   - Update statistics
   ↓
3. Weekly cleanup:
   DELETE FROM demo_sessions
   WHERE status = 'destroyed'
   AND destroyed_at < NOW() - INTERVAL '7 days'

Database Schema

demo_sessions Table

CREATE TABLE demo_sessions (
    id UUID PRIMARY KEY,
    session_id VARCHAR(100) UNIQUE NOT NULL,

    -- Ownership
    user_id UUID,
    ip_address VARCHAR(45),
    user_agent VARCHAR(500),

    -- Demo linking
    base_demo_tenant_id UUID NOT NULL,
    virtual_tenant_id UUID NOT NULL,
    demo_account_type VARCHAR(50) NOT NULL,

    -- Lifecycle
    status VARCHAR(20) NOT NULL,  -- active, expired, destroyed
    created_at TIMESTAMP WITH TIME ZONE NOT NULL,
    expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
    last_activity_at TIMESTAMP WITH TIME ZONE,
    destroyed_at TIMESTAMP WITH TIME ZONE,

    -- Metrics
    request_count INTEGER DEFAULT 0,
    data_cloned BOOLEAN DEFAULT FALSE,
    redis_populated BOOLEAN DEFAULT FALSE,

    -- Metadata
    metadata JSONB
);

CREATE INDEX idx_session_id ON demo_sessions(session_id);
CREATE INDEX idx_virtual_tenant ON demo_sessions(virtual_tenant_id);
CREATE INDEX idx_status ON demo_sessions(status);
CREATE INDEX idx_expires_at ON demo_sessions(expires_at);

tenants Table (Updated)

ALTER TABLE tenants ADD COLUMN is_demo BOOLEAN DEFAULT FALSE;
ALTER TABLE tenants ADD COLUMN is_demo_template BOOLEAN DEFAULT FALSE;
ALTER TABLE tenants ADD COLUMN base_demo_tenant_id UUID;
ALTER TABLE tenants ADD COLUMN demo_session_id VARCHAR(100);
ALTER TABLE tenants ADD COLUMN demo_expires_at TIMESTAMP WITH TIME ZONE;

CREATE INDEX idx_is_demo ON tenants(is_demo);
CREATE INDEX idx_demo_session ON tenants(demo_session_id);

Deployment

Initial Deployment

# 1. Deploy infrastructure (databases, redis, rabbitmq)
kubectl apply -k infrastructure/kubernetes/overlays/prod

# 2. Run migrations
# (Automatically handled by migration jobs)

# 3. Seed demo data
# (Automatically handled by demo-seed-* jobs)

# 4. Verify demo system
kubectl get jobs -n bakery-ia | grep demo-seed
kubectl logs -f job/demo-seed-users -n bakery-ia
kubectl logs -f job/demo-seed-tenants -n bakery-ia
kubectl logs -f job/demo-seed-inventory -n bakery-ia

# 5. Test demo session creation
curl -X POST http://your-domain/api/demo/session/create \
  -H "Content-Type: application/json" \
  -d '{"demo_account_type": "individual_bakery"}'

Using Tilt (Local Development)

# Start Tilt
tilt up

# Demo resources in Tilt UI:
# - databases: demo-session-db
# - migrations: demo-session-migration
# - services: demo-session-service
# - demo-init: demo-seed-users, demo-seed-tenants, demo-seed-inventory
# - config: patch-demo-session-env (sets CLONE_JOB_IMAGE dynamically)

# Tilt automatically:
# 1. Gets inventory-service image tag (e.g., tilt-abc123)
# 2. Patches demo-session-service with CLONE_JOB_IMAGE env var
# 3. Clone jobs use this image with IfNotPresent pull policy

Monitoring

Key Metrics

# Session Statistics
GET /api/demo/stats

{
  "total_sessions": 1250,
  "active_sessions": 45,
  "expired_sessions": 980,
  "destroyed_sessions": 225,
  "avg_duration_minutes": 18.5,
  "total_requests": 125000
}

Health Checks

# Demo Session Service
curl http://demo-session-service:8000/health

# Check active sessions
kubectl exec -it deployment/demo-session-service -- \
  python -c "from app.services import *; print(get_active_sessions())"

Logs

# Demo session service logs
kubectl logs -f deployment/demo-session-service -n bakery-ia

# Demo seed job logs
kubectl logs job/demo-seed-inventory -n bakery-ia

# Cleanup cron job logs
kubectl logs -l app=demo-cleanup -n bakery-ia --tail=100

Scaling Considerations

Current Limits

  • Concurrent Sessions: ~200 (2 replicas × ~100 sessions each)
  • Redis Memory: ~1-2 GB (10 MB per session × 200)
  • PostgreSQL: ~5-10 GB (30 MB per virtual tenant × 200)
  • Session Duration: 30 minutes (configurable)
  • Extensions: Maximum 3 per session

Scaling Up

# Scale demo-session-service
kubectl scale deployment/demo-session-service --replicas=4 -n bakery-ia

# Increase Redis memory (if needed)
# Edit redis deployment, increase memory limits

# Adjust session settings
# Edit demo-session configmap:
DEMO_SESSION_DURATION_MINUTES: 45  # Increase session time
DEMO_SESSION_MAX_EXTENSIONS: 5     # Allow more extensions

Security

Public Demo Credentials

Demo credentials are intentionally public for prospect access:

  • Published on marketing website
  • Included in demo documentation
  • Safe because sessions are isolated and ephemeral

Restrictions

  1. No Destructive Operations: DELETE blocked
  2. Limited Modifications: Only realistic testing operations
  3. No Sensitive Data Access: Cannot change passwords, billing, etc.
  4. Automatic Expiration: Sessions auto-destroy after 30 minutes
  5. Rate Limiting: Standard gateway rate limits apply
  6. No AI Training: Forecast API blocked for demo accounts (no trained models)
  7. Scheduler Prevention: Procurement scheduler filters out demo tenants

Data Privacy

  • No real customer data in demo tenants
  • Session data automatically deleted
  • Anonymized analytics only

Troubleshooting

Session Creation Fails

# Check demo-session-service health
kubectl get pods -l app=demo-session-service -n bakery-ia

# Check logs
kubectl logs deployment/demo-session-service -n bakery-ia --tail=50

# Verify base demo tenants exist
kubectl exec -it deployment/tenant-service -- \
  psql $TENANT_DATABASE_URL -c \
  "SELECT id, name, subdomain FROM tenants WHERE is_demo_template = true;"

Sessions Not Cleaning Up

# Check cleanup cronjob
kubectl get cronjobs -n bakery-ia
kubectl get jobs -l app=demo-cleanup -n bakery-ia

# Manually trigger cleanup
kubectl create job --from=cronjob/demo-session-cleanup manual-cleanup-$(date +%s) -n bakery-ia

# Check for orphaned sessions
kubectl exec -it deployment/demo-session-service -- \
  psql $DEMO_SESSION_DATABASE_URL -c \
  "SELECT status, COUNT(*) FROM demo_sessions GROUP BY status;"

Redis Connection Issues

# Test Redis connectivity
kubectl exec -it deployment/demo-session-service -- \
  python -c "import redis; r=redis.Redis(host='redis-service'); print(r.ping())"

# Check Redis memory usage
kubectl exec -it deployment/redis -- redis-cli INFO memory

Technical Implementation Details

Data Cloning Architecture

Choice: Kubernetes Job-based Cloning (selected over service-based endpoints)

Why K8s Jobs:

  • Database-level operations (faster than API calls)
  • Scalable (one job per session, isolated execution)
  • No service-specific clone endpoints needed
  • Works in both dev (Tilt) and production

How it Works:

  1. Demo-session-service creates K8s Job via K8s API
  2. Job uses CLONE_JOB_IMAGE environment variable (configured image)
  3. In Dev (Tilt): patch-demo-session-env sets dynamic Tilt image tag
  4. In Production: Deployment manifest has stable release tag
  5. Job runs clone_demo_tenant.py with imagePullPolicy: IfNotPresent
  6. Script uses ORM models to clone data safely

Environment-based Image Configuration:

# Demo-session deployment
env:
- name: CLONE_JOB_IMAGE
  value: "bakery/inventory-service:latest"  # Overridden by Tilt in dev

# Tilt automatically patches this to match actual inventory-service tag
# e.g., bakery/inventory-service:tilt-abc123

AI Model Restrictions

Fake Models in Database:

  • Demo tenants have AI model records in database
  • No actual model files (.pkl, .h5) stored
  • Forecast API blocked at gateway level for demo accounts
  • Returns user-friendly error message

Scheduler Prevention:

  • Procurement scheduler filters is_demo = true tenants
  • Prevents automated procurement runs on demo data
  • Manual procurement still allowed for realistic testing

Future Enhancements

  1. Analytics Dashboard: Track demo → paid conversion rates
  2. Guided Tours: In-app tutorials for demo users
  3. Custom Demo Scenarios: Let prospects choose specific features
  4. Demo Recordings: Capture anonymized session recordings
  5. Multi-Region: Deploy demo infrastructure in EU, US, LATAM
  6. Sales & Orders Cloning: Extend clone script to copy sales and orders data

References