GH GambleHub

Modèles de lecture et projections

Read Model est une table/index/vue spécialement conçue pour des lectures rapides sous un scénario de produit spécifique. Projection - processus qui convertit les événements/changements de source en mises à jour du modèle de lecture (généralement idempotent upsert). Associé au CQRS, il permet de décharger le noyau OLTP et de stabiliser les lectures p95/p99 en contrôlant la « fraîcheur ».

Idées principales :
  • Dénormaliser sous la demande, pas le « schéma universel ».
  • Mise à jour incrémentale et idempotente.
  • Gérer clairement la staleness et l'ordre.

1) Quand utiliser les modèles de lecture (et quand - pas)

Convient :
  • Lectures fréquentes et lourdes (joines/agrégations/tri) avec un délai de mise à jour acceptable.
  • Dashboards, catalogues, lendings, "top N', fides personnelles, listes de recherche.
  • Partage de charge : write-core - rigoureux, read-plan - rapide et évolutif.
Ne convient pas :
  • Opérations nécessitant des invariants stricts « pour chaque enregistrement » (argent, unicité). C'est un strong path.

2) Contour architectural (schéma verbal)

1. Source des changements : événements de domaine (event sourcing) ou CDC de OLTP.
2. Convoyeur de projection : parser → aggrégation/dénormalisation → idempotent upsert.
3. Read Store : Bases de données/index optimisées pour la demande (RDBMS, colonnes, moteurs de recherche).
4. API/client : SELECT/GET rapide, avec les attributs « as_of/freshness ».

3) Conception du modèle de lecture

Commencez par demander : quels champs, filtres, triage, pagination, top N ?
Dénormaliser : stocker les données déjà combinées (titres, montants, statuts).

Clés :
  • Lot : par 'tenant _ id', date, région.
  • Clé primaire : clé d'entreprise + baquet temporaire (par exemple, '(tenant_id, entity_id)' ou '(tenant_id, bucket_minute)').
  • Index : par where/order fréquents par.
  • TTL/rétention : pour les vitrines temporaires (par exemple 90 jours).

4) Flux de mises à jour et idempotence

Idempotent upsert est la base de la stabilité des projections.

Pseudo :
sql
-- Projection table
CREATE TABLE read_orders (
tenant_id  TEXT,
order_id  UUID,
status   TEXT,
total    NUMERIC(12,2),
customer  JSONB,
updated_at TIMESTAMP,
PRIMARY KEY (tenant_id, order_id)
);

-- Idempotent update by event
INSERT INTO read_orders(tenant_id, order_id, status, total, customer, updated_at)
VALUES (:tenant,:id,:status,:total,:customer,:ts)
ON CONFLICT (tenant_id, order_id) DO UPDATE
SET status = EXCLUDED. status,
total = EXCLUDED. total,
customer = COALESCE(EXCLUDED. customer, read_orders. customer),
updated_at = GREATEST(EXCLUDED. updated_at, read_orders. updated_at);
Règles :
  • Chaque message porte une version/heure ; nous ne prenons que « frais ou égal » (idempotency).
  • Pour les agrégats (compteurs, montants) - Stockez l'état et utilisez des mises à jour commutatives (ou des approches CRDT).

5) Source du changement : événements vs CDC

Événements (event sourcing) : riche sémantique, facile à construire différentes projections ; l'évolution des schémas est importante.
CDC (réplication logique) : se connecter simplement à une base de données existante ; le mappage des DML→sobyty et le filtrage des updates sonores seront nécessaires.

Les deux options nécessitent :
  • Garanties de livraison (at-least-once) et DLQ pour les messages « toxiques ».
  • Ordre par clé (partition key = 'tenant _ id : entity _ id').

6) Ordre, causalité et « fraîcheur »

Ordre par clé : les événements d'un objet doivent venir successivement ; utilisez le lot et les versions.
Causalité (session/causal) : pour que l'auteur voit ses changements (RYW), passez la version watermark dans les requêtes.
Fraîcheur (staleness bounded) : retournez 'as _ of '/' X-Data-Freshness'et tenez le SLO (p.ex. p95 ≤ 60c).

7) Agrégats incrémentiels et top N

Exemple de bacs de vente minutes :
sql
CREATE TABLE read_sales_minute (
tenant_id TEXT,
bucket  TIMESTAMP, -- toStartOfMinute revenue  NUMERIC(14,2),
orders  INT,
PRIMARY KEY (tenant_id, bucket)
);

-- Update by Event
INSERT INTO read_sales_minute(tenant_id, bucket, revenue, orders)
VALUES (:tenant,:bucket,:amount, 1)
ON CONFLICT (tenant_id, bucket) DO UPDATE
SET revenue = read_sales_minute. revenue + EXCLUDED. revenue,
orders = read_sales_minute. orders + 1;
Pour le top N :
  • Maintenez la vitrine classée (par exemple, « revenue DESC ») et ne mettez à jour que les positions modifiées (heap/skiplist/limited table).
  • Stockez la « fenêtre » du haut (par exemple, 100-1000 lignes par segment).

8) Recherches et géo-projections

Recherche (ES/Opensearch) : Document dénormalisé, pipeline de transformation, version du document = version source.
Géo : stockez 'POINT/LAT, LON', pré-agrégez les mystères/quadrotri.

9) Multi-tenants et régions

'tenant _ id' est obligatoire dans les clés des projections et des événements.
Fairness : limitez les projections per tenant (WFQ/DRR) afin que le « bruyant » ne freine pas les autres.
Résidence : la projection vit dans la même région que le noyau d'écriture ; vitrines interrégionales - agrégats/résumés.

10) Observabilité et SLO

Métriques :
  • « projection _ lag _ ms' (istochnik→vitrina), » freshness _ age _ ms' (depuis le dernier delta).
  • throughput updates, proportion d'erreurs, DLQ-rate, redrive-success.
  • Taille des vitrines, p95/p99 latence des lectures.
Tracing/logs :
  • Теги: `tenant_id`, `entity_id`, `event_id`, `version`, `projection_name`, `attempt`.
  • Annotations : solutions merge, ignorer les versions obsolètes.

11) Pleybooks (runbooks)

1. Croissance de la lagune : vérifier le connecteur/courtier, augmenter les lots, inclure la hiérarchisation des vitrines clés.
2. Beaucoup d'erreurs de schéma : geler la redrive, migrer les schémas (backfill), redémarrer avec la nouvelle version du mupper.
3. DLQ répétées : réduire le batch, activer le gestionnaire « shadow », augmenter l'idempotence.
4. Incohérence de la vitrine : effectuer des rebuild vitrines à partir du journal/source par fenêtre (sélectivement par tenant/partition).
5. Clés chaudes : limiter la concurrence par clé, ajouter des files d'attente locales, sortir l'unité dans une vitrine distincte.

12) Total recalculing (rebuild) et backfill

Approche :
  • Arrêter la consommation (ou passer à une nouvelle version de la vitrine).
  • Recalculer par lots (par lots/dates/tenants).
  • Activer le pull biphasé : d'abord remplir 'read __ v2', puis commuter atomiquement le routage des lectures.

13) Évolution des schémas (versioning)

'schema _ version 'dans les événements/documents.
La projection peut lire plusieurs versions, la migration à la volée.
Pour les changements majeurs - la nouvelle vitrine v2 et le trafic canarien.

14) Sécurité et accès

Hériter du RLS/ACL de la source ; ne faites pas une vitrine plus large d'accès que les données initiales.
Masquer le PII dans des projections inutiles pour UX/Analysis.
Vérification des révisions/recalculations/modifications manuelles.

15) Modèle de configuration

yaml projections:
read_orders:
source: kafka. orders. events partition_key: "{tenant_id}:{order_id}"
idempotency: version_ts upsert:
table: read_orders conflict_keys: [tenant_id, order_id]
freshness_slo_ms: 60000 dlq:
topic: orders. events. dlq redrive:
batch: 500 rate_limit_per_sec: 50 read_sales_minute:
source: cdc. orders partition_key: "{tenant_id}:{bucket_minute}"
aggregate: increment retention_days: 90 limits:
per_tenant_parallelism: 4 per_key_serial: true observability:
metrics: [projection_lag_ms, dlq_rate, redrive_success, read_p95_ms]

16) Erreurs typiques

« Une vitrine pour tous les cas » → des apdées lourdes et de mauvaises p99.
Manque d'idempotence → prises/sauts dans les unités.
Dual-write directement dans la vitrine et OLTP → divergence.
Zéro visibilité de fraîcheur → conflit d'attentes avec le produit.
Rebuild sans pull en deux phases → « trous » dans les réponses.
Pas de lots/indices → augmentation de la valeur et de la latence.

17) Recettes rapides

Catalogue/recherche : vitrine documentaire + upsert incrémental, lag ≤ 5-15 c, index sous filtres.
Dashboards : baquets minutes/heures, agrégats 'SUM/COUNT', p95 fraîcheur ≤ 60 c.
Bande personnelle : projection par utilisateur + causal/RYW pour l'auteur, fallback par cache.
Global SaaS : vitrines régionales, agrégats transrégionaux ; fairness per tenant.

18) Chèque-liste avant la vente

  • La vitrine est conçue pour répondre à une demande spécifique ; il y a des indices et des lots.
  • Source du changement choisie (événements/CDC) ; garanties de livraison et ordre par clé.
  • Idempotent upsert avec versions/temps ; protection contre les « anciens » événements.
  • Le SLO de fraîcheur est défini et donné dans les réponses (« as _ of/freshness »).
  • DLQ et redrive sécurisé sont personnalisés ; pleybuk sur rebuild/backfill.
  • Restrictions de concurrence (série per-key) et fairness per tenant.
  • Métriques de laga/error/latency, alertes à p95/p99 et croissance du DLQ.
  • Versionation des schémas et stratégie de migration (v2 + pull).
  • Les politiques d'accès/PII sont héritées et validées.

Conclusion

Les modèles et projections de lecture sont un accélérateur de lecture d'ingénierie : vous payez un petit prix de « fraîcheur » et d'infrastructure de streaming pour obtenir des millisecondes prévisibles et décharger le noyau des enregistrements. Concevez des vitrines à la demande, faites des apdées idempotentes, mesurez la larme et promettez clairement de la fraîcheur - et vos API resteront rapides même si la charge, les données et la géographie augmentent.

Contact

Prendre contact

Contactez-nous pour toute question ou demande d’assistance.Nous sommes toujours prêts à vous aider !

Commencer l’intégration

L’Email est obligatoire. Telegram ou WhatsApp — optionnels.

Votre nom optionnel
Email optionnel
Objet optionnel
Message optionnel
Telegram optionnel
@
Si vous indiquez Telegram — nous vous répondrons aussi là-bas.
WhatsApp optionnel
Format : +code pays et numéro (ex. +33XXXXXXXXX).

En cliquant sur ce bouton, vous acceptez le traitement de vos données.