Éducation en arabe à grande échelle sur AWS Lambda
6 min de lectureMohammad Shaker

Éducation en arabe à grande échelle sur AWS Lambda

Alphazed utilise AWS Lambda pour son backend, soutenant 95,000 élèves avec Flask, MySQL 8 sur RDS et un lac analytiques personnalisé.

Engineering

Réponse rapide

Alphazed utilise AWS Lambda pour son backend, soutenant 95,000 élèves avec Flask, MySQL 8 sur RDS et un lac analytiques personnalisé.

Alphazed gère tout son backend — desservant plus de 95,000 élèves dans plus de 50 pays — sur AWS Lambda avec le Framework Serverless. L'architecture utilise Flask sur Lambda derrière API Gateway, MySQL 8 sur RDS, S3 pour la distribution de contenu, et un lac analytique personnalisé (SQS → Kinesis Firehose → S3 → Glue → Athena). Les gestionnaires Lambda fins optimisent la latence de démarrage à froid, et le système dessert plus de 7 applications à partir d'une même base de code avec un changement de configuration au moment de l'exécution.

Pourquoi le serverless pour l'EdTech ?

Les applications éducatives ont des schémas d'utilisation imprévisibles :

  • Matins de semaine : les parents téléchargent l'application avant d'envoyer l'enfant à l'école (pic de trafic)
  • Après-midi de semaine : sessions de pratique après l'école (charge continue)
  • Weekends : sessions marathon intensives (charge 2-3x normale)
  • Pendant le Ramadan : l'utilisation en soirée explose (sessions familiales de lecture du Coran)
  • Vacances scolaires : schéma complètement différent

Avantages du serverless :

  • Paiement à la requête : Vous ne payez que pour l'utilisation réelle. Si 10 utilisateurs accèdent à l'API, vous payez pour 10 invocations. Si 100,000 utilisateurs y accèdent durant un moment viral, vous montez instantanément en échelle.
  • Pas de démarrages à froid pour les points de terminaison haute fréquence : Nous utilisons des couches Lambda "toujours chaudes" pour les points de terminaison fréquemment appelés.
  • Mise à l'échelle automatique : Gérez 10 utilisateurs simultanés ou 10,000 sans changement d'infrastructure.
  • Pas de maintenance serveur : L'équipe se concentre sur le programme et l'IA, pas sur les clusters Kubernetes ou les équilibrages de charge.

Analyse approfondie de l'architecture

API Gateway → Lambda → RDS

[Application Client] (iOS, Android, Web)
    ↓
[API Gateway] (routage HTTP, limitation du débit)
    ↓
[Gestionnaires Lambda] (application Flask, 512MB de mémoire, délai d'expiration de 28s)
    ├── Routes d'application : /app/* (points de terminaison mobiles)
    ├── Routes d'utilisateur : /user/* (points de terminaison authentifiés)
    └── Routes d'administration : /boss/* (tableau de bord administrateur)
    ↓
[MySQL 8 sur RDS] (Données persistantes)
    ↓
[Réponse] (JSON renvoyé au client)

Lambdas fines pour la vitesse

La plupart des Lambdas sont intentionnellement minimales :

# Gestionnaire fin (~100KB)
import json
import pymysql

def get_user_progress(event, context):
    user_id = event['pathParameters']['user_id']
    
    # Connexion directe à la DB (pas de surcharge ORM)
    conn = pymysql.connect(host='rds.aws.com', user='app', password='...', database='amal')
    cursor = conn.cursor()
    cursor.execute(
        'SELECT concept_id, accuracy FROM user_memory WHERE user_id = %s',
        (user_id,)
    )
    rows = cursor.fetchall()
    conn.close()
    
    return {
        'statusCode': 200,
        'body': json.dumps([{'concept': r[0], 'accuracy': r[1]} for r in rows])
    }

Pas d'importation de Flask, pas d'ORM SQLAlchemy, pas de middleware. Résultat : démarrage à froid en ~500ms contre 5-10s pour l'application Flask complète.

Les points de terminaison lourds (génération de contenu, traitement analytique) utilisent Flask complet :

# Gestionnaire lourd (~30MB avec Flask, SQLAlchemy, numpy)
from flask import Flask, jsonify
from models import UserMemory
import numpy as np

app = Flask(__name__)

@app.route('/content_duo/generate', methods=['POST'])
def generate_content_duo():
    # Logique complexe nécessitant l'ORM
    user = UserMemory.query.filter_by(user_id=request.json['user_id']).first()
    # ... générer session personnalisée ...
    return jsonify(session_data)

Compromis : les démarrages à froid sont plus lents, mais ceux-ci sont appelés moins fréquemment.

Préfixation par application pour les tables

Un exemplaire RDS dessert 7+ applications avec l'isolation au niveau des bases de données :

-- Application Amal
CREATE TABLE amal_users (...)
CREATE TABLE amal_content_bytes (...)
CREATE TABLE amal_user_memory (...)

-- Application Thurayya
CREATE TABLE thurayya_users (...)
CREATE TABLE thurayya_content_bytes (...)
CREATE TABLE thurayya_user_memory (...)

-- Autres applications : qais_*, kiDelite_*, etc.

Au moment du déploiement, la variable d'environnement APP_NAME sélectionne le préfixe :

app_name = os.getenv('APP_NAME', 'amal')  # 'amal', 'thurayya', 'qais', etc.

# Les requêtes utilisent dynamiquement le préfixe
nom_table = f'{app_name}_users'
cursor.execute(f'SELECT * FROM {nom_table} WHERE id = %s', (user_id,))

Le lac analytique

Problème : Les requêtes directes à la base de données pour les analyses ralentissent la production. L'exécution de rapports verrouille les tables.

Solution : Pipeline analytique asynchrone

[Application mobile]
    ↓ (envoie l'événement)
[Point de terminaison API] → [File d'attente SQS] (asynchrone)
    ↓ (répond immédiatement à l'application)
    ↓ (n'attend pas les analyses)
[Kinesis Firehose] (regroupe les événements toutes les 5 min ou lors de l'atteinte de 100MB)
    ↓
[S3] (partitionné : s3://analytics-lake/amal/2026/03/28/events.parquet)
    ↓
[AWS Glue] (récupère les données de S3, déduit le schéma)
    ↓
[Athena] (requêtes SQL via le moteur Presto)
    ↓
[Tableau de bord] (affiche les insights en temps réel)

Modèle de File d'attente de lettres mortes (DLQ)

Si les analyses échouent :

SQS → [Échec de Firehose]
  ↓
  [DLQ reçoit les messages échoués]
  ↓
  [Alerte envoyée aux opérations]
  ↓
  [L'API de production n'est pas affectée]

Les analyses ne bloquent jamais les requêtes des utilisateurs. Les enfants peuvent apprendre même si le pipeline analytique est en panne.

Stratégies d'optimisation des coûts

Stratégie 1 : Lambdas fines pour les points de terminaison haute fréquence

  • L'application mobile typique effectue 10-20 appels d'API par session
  • 95,000 utilisateurs actifs × 3 sessions/jour × 15 appels/session = 4,275M appels/jour
  • Si chaque appel coûte $0,0000002 (tarification Lambda), cela représente $0,86/jour
  • Réduire le temps de démarrage à froid de 10s économise ~500$/mois

Stratégie 2 : Instances réservées RDS

  • Engagement de réservation sur 3 ans : ~60% de réduction par rapport à la demande
  • Nous utilisons `db.r6i.xlarge` (4 vCPU, 32GB RAM): $2,800/mois réservés contre $6,500/mois à la demande
  • Économies annuelles : ~50,000$

Stratégie 3 : Mise en cache

  • Données fréquemment consultées (programme, octets de contenu) mises en cache dans ElastiCache (Redis)
  • Réduit les requêtes RDS de 70%
  • Coût : 800$/mois pour le cache, économise 2,000$/mois en RDS

Soutenir plus de 7 applications à partir d'une base de code unique

ApplicationPréfixeTables DBPile LambdaStatut
Amal`amal_`40+ tablesPartagéProduction
Thurayya`thurayya_`40+ tablesPartagéProduction
Qais`qais_`35+ tablesPartagéBêta
KidElite`kidelite_`40+ tablesPartagéProduction
Alphazed School`school_`50+ tablesPartagéBêta
Alphazed Montessori`montessori_`45+ tablesPartagéInterne

Un backend, un pipeline de déploiement, 6 applications simultanées. Lancement d'une nouvelle application : semaines au lieu de mois.

FAQ

Q : Lambda n'a-t-il pas une limite de délai d'attente de 15 minutes ? R : Lambda a un délai d'attente maximum de 15 minutes, mais nous avons rarement besoin de requêtes de longue durée. Les charges lourdes (génération de contenu, exportations volumineuses) utilisent des tâches asynchrones avec SQS + Step Functions.

Q : Que se passe-t-il si la base de données tombe en panne ? R : RDS a un basculement Multi-AZ (réplica principal + de secours). Le basculement est automatique et prend ~60 secondes. Les clients voient de brèves expirations, mais la récupération est rapide.

Q : Comment gérez-vous le regroupement des connexions à la base de données avec Lambda sans état ? R : Chaque instance Lambda maintient un pool de connexions (réutilisé à travers les invocations chaudes). Les démarrages à froid obtiennent de nouvelles connexions. RDS Proxy se place entre Lambda et RDS pour gérer les limites de connexion.

Articles connexes