PostgreSQL vs Aurora PostgreSQL : surprises de coût lors des pics (et comment les éviter)

Cet article vous a aidé ?

La panne est terminée. La latence est redevenue normale. Tout le monde se félicite sur Slack. Puis la finance arrive avec un graphique en forme de saut à ski et demande pourquoi la facture de la base de données a l’air d’avoir voulu échapper à la gravité terrestre.

Les charges en pics ne cassent pas seulement les systèmes ; elles brisent les hypothèses. Et sur AWS, elles peuvent casser les budgets d’une manière qui paraît personnelle. Parlons des différences entre PostgreSQL sur vos propres machines (ou même auto-géré sur EC2), RDS PostgreSQL et Aurora PostgreSQL pendant les pics — et des réglages qui empêchent réellement les surprises de coûts sans saboter la fiabilité.

La taxe des pics : où l’argent fuit pendant les rafales

La plupart des histoires de « surprise de coût » ne portent pas sur le fait d’avoir choisi le mauvais type d’instance. Elles concernent une charge qui change de forme pendant quelques minutes, puis la plateforme vous facture pour tous les effets secondaires : plus d’E/S, plus de réplicas, plus de volume de journaux, plus de tempêtes de retries, plus de trafic inter-AZ, plus d’amplification de lecture due à des « correctifs » qui n’en étaient pas.

Lors d’un pic, votre base de données peut devenir un multiplicateur. Une hausse de trafic 3× devient une hausse d’E/S 10× parce que les caches ratent, les requêtes dévient sur le disque et votre application réessaie sur timeout comme si elle passait une audition pour un rôle de déni de service.

La distinction importante :

  • PostgreSQL traditionnel (sur site ou EC2) vous facture principalement la capacité provisionnée. Lors d’un pic, vous payez plutôt en douleur (latence, saturation, pannes) qu’en euros — sauf si vous redimensionnez automatiquement l’infrastructure.
  • Aurora PostgreSQL vous facture à la fois la capacité et la consommation selon des métriques faciles à ignorer jusqu’à ce qu’elles ne le soient plus. Les pics peuvent se traduire directement par des dépenses mesurées : E/S, croissance du stockage, unités de capacité serverless, rétention des sauvegardes, et parfois des schémas de trafic réseau inconnus.

Aucun n’est « moins cher » par défaut. Les deux peuvent l’être. Ce qui change, c’est quel mode de défaillance apparaît en premier : l’indisponibilité ou la facture.

Faits et historique utiles qui expliquent les factures d’aujourd’hui

Ce ne sont pas des anecdotes. Ce sont les raisons pour lesquelles certaines surprises de coût se répètent.

  1. PostgreSQL existe depuis le milieu des années 1990, et il a hérité d’hypothèses de conception d’une époque où le disque était lent, la RAM coûteuse et « le cloud » voulait dire la météo.
  2. Amazon RDS a été lancé en 2009 pour réduire le coût opérationnel de gestion des bases de données — patchs, sauvegardes et basculements. Il n’a pas supprimé l’ingénierie de performance ; il a juste déplacé quelques boutons.
  3. Aurora a débarqué en 2014 avec une couche de stockage distribuée conçue pour améliorer la durabilité et le débit. Elle a aussi créé de nouvelles surfaces de facturation — surtout autour des E/S et du comportement du stockage.
  4. Le stockage d’Aurora s’auto-dimensionne au lieu de vous forcer à pré-provisionner une taille de volume. C’est agréable opérationnellement et dangereux financièrement si la croissance vient du bloat, d’une rétention incontrôlée ou d’une table oubliée.
  5. Le modèle MVCC de PostgreSQL signifie que les mises à jour ne remplacent pas les lignes ; elles créent de nouvelles versions. Pendant les pics, cela peut générer plus de WAL, plus de pression sur autovacuum et plus de churn de stockage.
  6. La rétention des WAL est une ligne de budget silencieuse dans de nombreuses offres managées. Une réplique bloquée ou une transaction longue peut forcer la croissance de la rétention et augmenter l’utilisation du stockage.
  7. Le pooling de connexions s’est généralisé dans les environnements Postgres parce que le modèle processus-par-connexion est robuste mais coûteux. Pics + pas de pooling = souvent consommation CPU et mémoire élevée.
  8. Aurora Serverless a connu deux générations (v1 et v2). La v2 a réduit certaines bizarreries d’échelle, mais la facturation suit toujours la capacité dans le temps et peut augmenter plus vite que prévu lors de tempêtes de connexions.
  9. La plupart des explosions de coût corrèlent avec des incidents de fiabilité parce que retries, basculements, caches ratés et actions de scaling d’urgence créent tous de la consommation mesurée. Le coût est souvent la deuxième alarme après la latence.

Une citation pour rester honnête : idée paraphrasée de John Allspaw : Les incidents arrivent parce que les systèmes sont complexes ; la résilience vient de l’apprentissage et de l’amélioration, pas du blâme.

Deux modèles mentaux : « une machine avec des disques » vs « un service avec compteurs »

PostgreSQL (auto-géré) : vous payez principalement pour être prêt

Quand vous exécutez Postgres vous-même (bare metal, VM ou EC2), le coût est largement lié aux ressources provisionnées : CPU, RAM et stockage. Les pics ne changent pas beaucoup votre facture à moins que vous ne fassiez du scale-out (nouvelles réplicas) ou du scale-up (instances plus grosses) dynamiquement.

L’avantage, c’est la prévisibilité. L’inconvénient, c’est que vous payez pour de la marge toute l’année afin de survivre au pic de 30 minutes du Black Friday — ou vous ne le faites pas, et vos clients vivent l’esprit des fêtes via des erreurs 500.

Aurora PostgreSQL : vous payez pour être prêt et pour ce que vous consommez

Aurora introduit une séparation entre le compute et le stockage qui peut être excellente pour la disponibilité et l’échelle des lectures. Mais le modèle économique ressemble davantage à un « compteur d’utilité » qu’à une location. Pendant les pics, les compteurs tournent.

Dans Aurora, les catégories de surprises les plus courantes sont :

  • Décisions de montée en charge du compute (y compris réplicas et capacité serverless).
  • Consommation d’E/S, y compris les lectures causées par des caches manqués et des requêtes inefficaces.
  • Croissance du stockage due au bloat, aux WAL, à l’utilisation de fichiers temporaires et aux politiques de rétention des sauvegardes.
  • Effets réseau et inter-AZ, surtout quand l’architecture déplace involontairement des données.

Blague n°1 : La facturation d’Aurora, c’est comme un abonnement à une salle de sport qui facture aussi par pas sur le tapis roulant. Vous allez vous mettre en forme, mais vous vous mettrez aussi à réfléchir à marcher moins.

Surprises de coût par catégorie (et comment elles se manifestent)

1) La surprise « on a ajouté des réplicas »

Pendant un pic, les équipes ajoutent souvent des réplicas de lecture ou redimensionnent des instances. En Postgres auto-géré, cela signifie en général de nouvelles instances EC2 (ou plus grosses). Sur Aurora, les réplicas de lecture peuvent être lancés rapidement, ce qui est bien. Mais « rapide » veut aussi dire « facile à faire sur un coup de tête ».

Schéma de surprise : les réplicas deviennent collants. Le pic se termine, les réplicas restent, et maintenant vous payez un nouveau niveau de base.

Comment l’éviter :

  • Mettez le nombre de réplicas sous politique explicite : max N, réduction après X minutes de stabilité.
  • Instrumez le lag des réplicas et l’utilisation des endpoints lecteurs pour savoir si les réplicas ont réellement aidé.
  • Privilégiez l’optimisation des requêtes et le cache pour les pics répétitifs ; utilisez les réplicas pour des charges de lecture soutenues.

2) La surprise « les E/S sont non linéaires »

Les pics amplifient l’inefficacité. Une requête médiocre sans index peut être « acceptable » à 50 QPS et catastrophique à 500 QPS. Sur Aurora, la catastrophe arrive souvent sous la forme d’une ligne de facture E/S en plus d’un incident de latence.

Causes courantes d’E/S non linéaires durant les pics :

  • Caches froids après basculement ou événements de scaling.
  • Scans larges dus à de mauvais plans (sensibilité aux paramètres, statistiques obsolètes, mauvais index).
  • Sorts/hash qui dévient sur disque à cause d’un work_mem insuffisant ou de jeux de résultats énormes.
  • Patrons N+1 qui se multiplient par requête sous forte concurrence.

Détail clé : certaines optimisations réduisent le CPU mais augmentent les E/S, et vous pouvez vous « optimiser » vers une facture plus élevée. Nous raconterons une histoire à ce sujet.

3) Croissance du stockage : auto-scale ne veut pas dire auto-nettoyage

Le stockage d’Aurora croît automatiquement. Super. Mais il ne diminue pas automatiquement comme la plupart des gens l’espèrent émotionnellement, surtout si la croissance vient du bloat, de grosses tables ou de pics temporaires de rétention WAL.

La croissance du stockage pendant les pics peut provenir de :

  • Bloat MVCC quand les mises à jour/suppressions augmentent et que vacuum ne suit pas.
  • Transactions longues empêchant vacuum de récupérer des tuples.
  • Slots de réplication logique maintenant les WAL.
  • Fichiers temporaires liés aux déversements sur disque (généralement un signe de performance aussi).

En Postgres auto-géré, vous voyez le disque se remplir et paniquez. En Aurora, vous voyez la facture plus tard et paniquez avec un meilleur éclairage.

4) Scaling serverless : la facture suit la concurrence, pas vos intentions

Aurora Serverless v2 peut convenir aux charges en rafales. Il peut aussi être cher si les rafales sont causées par des tempêtes de connexions, des retries ou un comportement « bavard » des applications.

Mécanisme de surprise : la capacité s’ajuste pour satisfaire des signaux de demande (connexions, CPU, pression mémoire). Un pic causé par un mauvais comportement client ressemble à un pic causé par une vraie croissance business. La base ne se soucie pas de la raison.

5) Sauvegardes et rétention : ennuyeux jusqu’à ce que ce ne le soit plus

Les coûts de sauvegarde ne sont généralement pas la plus grosse ligne, mais ils deviennent visibles quand le stockage croît rapidement ou que les politiques de rétention sont « réglées et oubliées ». Pendant les pics, si vous écrivez plus (charges riches en WAL), vous pouvez aussi augmenter l’activité de sauvegarde et de snapshot selon votre configuration.

L’astuce est que la dépense de sauvegarde est souvent une surprise différée — des semaines après l’incident qui a causé la croissance du stockage.

6) Surprises réseau inter-AZ et « réseau invisible »

Certaines architectures génèrent beaucoup de trafic inter-AZ : réplication, clients dans différentes AZ, analytics extrayant de gros jeux de données ou jobs batch déplaçant des données hors région. Pendant les pics, ces flux augmentent.

Conseil pratique : gardez les clients applicatifs dans la même AZ que leur endpoint primaire quand c’est possible, et soyez intentionnel quant à l’emplacement de vos lecteurs. La fiabilité multi-AZ est bonne ; la discussion inter-AZ accidentelle ne l’est pas.

Blague n°2 : Rien ne vous apprend à aimer le cache comme le fait de payer pour la même donnée lue 10 millions de fois en une journée.

Mode d’emploi pour un diagnostic rapide

Quand les coûts grimpent, vous avez généralement un incident de performance qui se cache. Diagnostiquez comme un SRE : trouvez le goulet, puis mappez-le à la dimension de facturation.

Premier : confirmez ce qui a changé

  • Forme du trafic : QPS, concurrence, mix de requêtes. Était-ce plus d’utilisateurs ou plus de retries ?
  • Topologie de la base : nouveaux réplicas, basculement, événements de scaling, changements de paramètres.
  • Déploiements : release applicative, changement de schéma, nouvel index, nouveau chemin de requête.

Deuxième : localisez le goulet

  • Limité par le CPU : CPU élevé, faible attente I/O, requêtes lentes dues à des opérateurs gourmands en calcul.
  • Limité par les E/S : latence lecture/écriture élevée, manque de hits dans le buffer cache, croissance des fichiers temporaires.
  • Limité par les verrous : sessions bloquées, transactions longues, échecs de sérialisation, deadlocks.
  • Limité par les connexions : trop de connexions, timeouts, handshakes d’auth fréquents, pression mémoire.

Troisième : mappez le goulet aux leviers de coût

  • CPU bound → scaling compute, classe d’instance plus grande, augmentation ACU serverless.
  • I/O bound → facturation E/S Aurora, croissance du stockage due au churn, E/S de déversement temporaire.
  • Lock bound → retries accrus, charge amplifiée, plus d’écritures (WAL), parfois plus de réplicas ajoutés à tort pour « corriger » les lectures.
  • Connection bound → montée forcée en capacité, ajout de réplicas, augmentation ACU, pire comportement de cache après redémarrages/basculements.

Quatrième : arrêtez l’hémorragie en sécurité

  • Activez ou resserrez le pooling des connexions ; plafonnez les connexions côté app.
  • Limitez le débit des endpoints les plus bruyants.
  • Désactivez le comportement « retry immédiatement sans limite » ; ajoutez du jitter et des budgets.
  • Si vous devez ajouter des réplicas, définissez un minuteur de suppression et mesurez l’utilisation réelle des lecteurs.

Tâches pratiques : commandes, sorties et décisions (12+)

Voici les tâches que j’exécute réellement pendant et après les pics. Chaque tâche inclut : une commande, ce que signifie une sortie typique et la décision associée.

Task 1: Identify top queries by total time (pg_stat_statements)

cr0x@server:~$ psql "$DATABASE_URL" -c "
SELECT queryid,
       calls,
       round(total_exec_time::numeric, 2) AS total_ms,
       round(mean_exec_time::numeric, 2) AS mean_ms,
       rows
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;"
 queryid  | calls | total_ms  | mean_ms | rows
----------+-------+-----------+---------+-------
 91283412 | 80000 | 980000.12 |   12.25 | 400000
 77221110 |  3000 | 410000.55 |  136.66 |   3000
(2 rows)

Ce que cela signifie : La première requête a consommé le plus de temps total, même si la latence moyenne n’est pas terrible. Lors des pics, « modérément lente mais massivement fréquente » est souvent le coupable.

Décision : Commencez par la requête avec le plus de temps total. Obtenez son plan, ajoutez le bon index ou réduisez le volume d’appels (batching, cache). Ne poursuivez pas la requête la plus lente à moins qu’elle ne soit fréquente.

Task 2: Get an execution plan with buffers (to see I/O)

cr0x@server:~$ psql "$DATABASE_URL" -c "
EXPLAIN (ANALYZE, BUFFERS, VERBOSE)
SELECT * FROM orders
WHERE customer_id = 12345
ORDER BY created_at DESC
LIMIT 50;"
                                                                QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=0.56..25.12 rows=50 width=128) (actual time=2.114..5.882 rows=50 loops=1)
   Buffers: shared hit=120 read=450
   ->  Index Scan Backward using orders_customer_created_idx on public.orders  (cost=0.56..1200.00 rows=2500 width=128)
       Index Cond: (orders.customer_id = 12345)
       Buffers: shared hit=120 read=450
 Planning Time: 0.210 ms
 Execution Time: 6.050 ms

Ce que cela signifie : « shared read=450 » indique des lectures physiques. Pendant les pics, ce nombre explose si les caches sont froids ou si la sélectivité de l’index est mauvaise.

Décision : Si les lectures dominent, corrigez les schémas d’accès (meilleurs index, jeux de résultats plus petits, cache) et évitez de « simplement ajouter des réplicas » comme première réponse.

Task 3: Check cache hit ratio (rough signal, not religion)

cr0x@server:~$ psql "$DATABASE_URL" -c "
SELECT datname,
       round(100.0 * blks_hit / nullif(blks_hit + blks_read, 0), 2) AS cache_hit_pct
FROM pg_stat_database
WHERE datname = current_database();"
 datname | cache_hit_pct
---------+---------------
 appdb   |         93.41

Ce que cela signifie : Une chute par rapport à votre baseline habituelle corrèle souvent avec une hausse des E/S Aurora. Le ratio de hit du cache n’est pas une KPI, mais c’est un détecteur de fumée.

Décision : Si le ratio a chuté pendant le pic, investiguez les déclencheurs de cache froid (basculement, scaling, changement de mix de requêtes) et envisagez des stratégies de réchauffement ou une taille de compute plus stable.

Task 4: Detect connection storms and who’s doing it

cr0x@server:~$ psql "$DATABASE_URL" -c "
SELECT usename, application_name, state, count(*)
FROM pg_stat_activity
GROUP BY 1,2,3
ORDER BY 4 DESC
LIMIT 10;"
 usename | application_name | state  | count
--------+-------------------+--------+-------
 app    | api               | active |   220
 app    | api               | idle   |   900
 app    | worker            | active |    80

Ce que cela signifie : 900 sessions idle est un pool qui n’est pas poolé, ou un client qui croit que créer des connexions est un hobby.

Décision : Mettez en place PgBouncer (ou RDS Proxy lorsque c’est approprié), plafonnez le nombre maximum de connexions et corrigez le dimensionnement du pool côté app. Si vous êtes sur Aurora Serverless v2, traitez le nombre de connexions comme un signal de coût.

Task 5: Identify the longest-running transactions (vacuum blockers)

cr0x@server:~$ psql "$DATABASE_URL" -c "
SELECT pid,
       now() - xact_start AS xact_age,
       wait_event_type,
       state,
       left(query, 80) AS query
FROM pg_stat_activity
WHERE xact_start IS NOT NULL
ORDER BY xact_age DESC
LIMIT 10;"
 pid  |  xact_age  | wait_event_type | state  | query
------+------------+-----------------+--------+-------------------------------
 4412 | 02:14:09   | Client          | idle   | BEGIN;
 9871 | 00:09:33   | Lock            | active | UPDATE orders SET status='X'

Ce que cela signifie : Une transaction ouverte depuis 2 heures peut forcer la rétention WAL et le bloat, et augmenter le stockage/E/S après le pic.

Décision : Corrigez le pattern applicatif (pas d’idle-in-transaction), définissez des timeouts de statement/idle et envisagez de tuer les coupables pendant les incidents.

Task 6: Find lock contention quickly

cr0x@server:~$ psql "$DATABASE_URL" -c "
SELECT blocked.pid AS blocked_pid,
       blocker.pid AS blocker_pid,
       now() - blocker.query_start AS blocker_age,
       left(blocker.query, 60) AS blocker_query
FROM pg_locks blocked
JOIN pg_stat_activity blocked_act ON blocked.pid = blocked_act.pid
JOIN pg_locks blocker ON blocker.locktype = blocked.locktype
  AND blocker.database IS NOT DISTINCT FROM blocked.database
  AND blocker.relation IS NOT DISTINCT FROM blocked.relation
  AND blocker.page IS NOT DISTINCT FROM blocked.page
  AND blocker.tuple IS NOT DISTINCT FROM blocked.tuple
  AND blocker.transactionid IS NOT DISTINCT FROM blocked.transactionid
  AND blocker.pid != blocked.pid
JOIN pg_stat_activity blocker ON blocker.pid = blocker.pid
WHERE NOT blocked.granted
LIMIT 5;"
 blocked_pid | blocker_pid | blocker_age | blocker_query
------------+------------+-------------+------------------------------
      12011 |      11888 | 00:03:12    | ALTER TABLE orders ADD COLUMN

Ce que cela signifie : Du DDL pendant le pic cause souvent des files d’attente de verrous, ce qui provoque des retries, qui génèrent de la charge, qui génèrent du coût.

Décision : Déplacez le DDL bloquant en heures creuses, utilisez des patterns de migration plus sûrs et limitez le lock timeout pour que les clients échouent vite au lieu de se ruer.

Task 7: Check temp file usage (disk spills = extra I/O)

cr0x@server:~$ psql "$DATABASE_URL" -c "
SELECT datname,
       temp_files,
       pg_size_pretty(temp_bytes) AS temp_written
FROM pg_stat_database
WHERE datname = current_database();"
 datname | temp_files | temp_written
---------+------------+--------------
 appdb   |      18220 | 48 GB

Ce que cela signifie : 48 GB d’écritures temporaires pendant un pic est un panneau néon. Cela corrèle souvent avec des tris/hashes coûteux et peut augmenter les frais E/S Aurora.

Décision : Corrigez la requête et/ou augmentez la mémoire prudemment (work_mem par session peut exploser), et réduisez la concurrence via le pooling.

Task 8: Verify autovacuum is keeping up (bloat prevention)

cr0x@server:~$ psql "$DATABASE_URL" -c "
SELECT relname,
       n_dead_tup,
       n_live_tup,
       last_autovacuum,
       last_autoanalyze
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT 10;"
 relname | n_dead_tup | n_live_tup |     last_autovacuum      |     last_autoanalyze
---------+------------+------------+--------------------------+--------------------------
 events  |   9200000  |  11000000  |                          | 2025-12-30 08:12:40+00

Ce que cela signifie : Des tuples morts énormes et aucun autovacuum récent indiquent que l’autovacuum est à la traîne — classique croissance de stockage post-pic et régression de performance.

Décision : Tenez l’autovacuum pour les tables chaudes, ajoutez des index qui réduisent le churn et éliminez les transactions longues qui bloquent le nettoyage.

Task 9: Estimate table bloat (quick-and-dirty)

cr0x@server:~$ psql "$DATABASE_URL" -c "
SELECT relname,
       pg_size_pretty(pg_total_relation_size(relid)) AS total_size,
       n_dead_tup
FROM pg_stat_user_tables
ORDER BY pg_total_relation_size(relid) DESC
LIMIT 10;"
 relname | total_size | n_dead_tup
---------+------------+-----------
 events  | 180 GB     | 9200000
 orders  |  95 GB     |  120000

Ce que cela signifie : Une table massive avec beaucoup de tuples morts est un multiplicateur de stockage et d’E/S.

Décision : Envisagez le partitionnement, des réglages autovacuum plus agressifs ou une maintenance périodique (comme VACUUM (FULL) dans les cas extrêmes, mais planifiez l’impact). Sur Aurora, faites cela avant que la croissance du stockage ne paraisse permanente.

Task 10: Check replication lag (replicas that can’t keep up)

cr0x@server:~$ psql "$DATABASE_URL" -c "
SELECT application_name,
       client_addr,
       state,
       write_lag,
       flush_lag,
       replay_lag
FROM pg_stat_replication;"
 application_name | client_addr |  state  | write_lag | flush_lag | replay_lag
-----------------+-------------+---------+-----------+-----------+------------
 aurora-replica-1 | 10.0.2.55   | streaming | 00:00:01 | 00:00:02 | 00:00:05

Ce que cela signifie : Un petit lag est acceptable. Un fort lag pendant les pics peut causer des retries, des lectures obsolètes et une croissance de la rétention WAL.

Décision : Si le lag augmente, réduisez la pression d’écriture, corrigez les transactions longues et ne routez pas les lectures sensibles à la latence vers des réplicas en retard.

Task 11: Identify WAL volume growth (write amplification)

cr0x@server:~$ psql "$DATABASE_URL" -c "
SELECT pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), '0/0')) AS wal_since_boot;"
 wal_since_boot
----------------
 320 GB

Ce que cela signifie : Un volume WAL élevé peut corréler avec un churn de stockage plus important, une pression sur la réplication et un surcoût des sauvegardes.

Décision : Réduisez les mises à jour inutiles, regroupez les écritures et évitez les patterns de mise à jour massive de grandes lignes. Envisagez des ajustements de fillfactor pour les patrons de mise à jour intensifs.

Task 12: Check for missing indexes via slow queries (triage)

cr0x@server:~$ psql "$DATABASE_URL" -c "
SELECT schemaname, relname, seq_scan, idx_scan,
       pg_size_pretty(pg_relation_size(relid)) AS table_size
FROM pg_stat_user_tables
WHERE seq_scan > 10000 AND idx_scan = 0
ORDER BY seq_scan DESC
LIMIT 10;"
 schemaname | relname | seq_scan | idx_scan | table_size
------------+---------+----------+----------+-----------
 public     | events  |   820000 |        0 | 120 GB

Ce que cela signifie : Une grosse table avec d’innombrables scans séquentiels est une source probable de pics d’E/S.

Décision : Ajoutez des index ciblés et réécrivez les requêtes. Validez avec EXPLAIN et des statistiques proches de la production ; ne créez pas d’index à l’aveugle.

Task 13: Verify Postgres settings that cause surprise memory blowups

cr0x@server:~$ psql "$DATABASE_URL" -c "SHOW work_mem; SHOW max_connections; SHOW shared_buffers;"
 work_mem
---------
 64MB
 max_connections
-----------------
 2000
 shared_buffers
----------------
 8GB

Ce que cela signifie : 64MB de work_mem avec 2000 connexions n’est pas « sûr pour 128GB RAM ». C’est « profitez des échanges et des déversements sur disque ».

Décision : Réduisez max_connections (pool), définissez un work_mem raisonnable et dimensionnez le compute pour la concurrence réelle plutôt que théorique.

Task 14: On a Linux Postgres host, confirm I/O wait and saturation (self-managed)

cr0x@server:~$ iostat -xz 1 3
Linux 6.1.0 (db01)  12/30/2025  _x86_64_  (8 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          22.10    0.00    6.12   32.55    0.00   39.23

Device            r/s     w/s   rkB/s   wkB/s  await  svctm  %util
nvme0n1         980.0   410.0 52000.0 18000.0  18.2   0.9   98.7

Ce que cela signifie : Un iowait élevé et ~99% d’utilisation suggèrent que vous êtes lié par les E/S. Sur EC2/en local, c’est souvent la vraie limitation ; sur Aurora vous le voyez comme de la latence + une facturation E/S.

Décision : Réduisez la demande d’E/S (index, corrections de requêtes) ou augmentez la performance du stockage (disques/IOPS plus rapides). N’ajoutez pas du CPU si le disque est le mur.

Task 15: On a Linux Postgres host, find top talkers (connections and ports)

cr0x@server:~$ ss -tn sport = :5432 | head
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0      0      10.0.1.10:5432     10.0.4.22:52144
ESTAB 0      0      10.0.1.10:5432     10.0.4.22:52146
ESTAB 0      0      10.0.1.10:5432     10.0.9.17:60311

Ce que cela signifie : Vous pouvez rapidement repérer quelles machines applicatives ouvrent le plus de connexions TCP pendant une tempête.

Décision : Limitez la création de connexions à la source : réglages du pool applicatif, poolers en sidecar ou shedding de charge. Empêchez le « scale horizontal » de devenir une multiplication horizontale des connexions.

Trois mini-histoires d’entreprise tirées du terrain

Mini-histoire 1 : L’incident causé par une mauvaise hypothèse

Une société SaaS de taille moyenne a migré d’un PostgreSQL auto-géré sur EC2 vers Aurora PostgreSQL. L’argument était solide : moins d’opérations, meilleure durabilité du stockage, gestion plus facile des réplicas. L’équipe avait aussi un pic récurrent : chaque heure pile, les clients rafraîchissaient les tableaux de bord et des jobs en arrière-plan se déclenchaient.

La mauvaise hypothèse était subtile : « le stockage Aurora s’auto-dimensionne, donc on n’a pas à penser au disque. » Ils ont cessé de suivre les métriques de bloat et d’autovacuum aussi agressivement qu’avant. Pas par paresse — par soulagement. Les alarmes disque étaient psychologiquement associées à l’ancien monde.

Lors d’une semaine particulièrement chargée, le pic horaire est devenu un pattern quotidien de forte écriture. Une table stockant l’état des événements était fréquemment mise à jour et l’autovacuum n’a pas suivi. De longues transactions analytiques (lecture seule, mais ouvertes longtemps) empêchaient le nettoyage. Le stockage a rapidement augmenté, puis est resté élevé.

L’incident a commencé par de la latence : plus de lectures nécessaires à cause du churn de cache et d’index gonflés. Puis c’est devenu un événement de coût : le compteur E/S a grimpé et la ligne stockage a sauté. La finance n’avait pas tort d’être surprise ; le système s’est comporté comme conçu : il a continué de fonctionner et de stocker.

Le correctif n’était pas un réglage Aurora magique. C’était de l’hygiène Postgres traditionnelle : tuer les sessions idle-in-transaction, tuner autovacuum pour la table chaude, ajouter un index plus sélectif pour réduire les scans et modifier le modèle de données pour diminuer le churn des mises à jour. Après cela, la croissance du stockage a ralenti. La facture a cessé de monter. Et l’équipe a réappris une vérité ennuyeuse : base managée ne veut pas dire données managées.

Mini-histoire 2 : L’optimisation qui s’est retournée contre eux

Une autre entreprise avait une API à lecture majoritaire et des pics récurrents. Quelqu’un a fait une chose sensée : ajouter un index pour accélérer une requête lente. Ça a marché. La latence P95 est tombée. L’équipe a fêté et est passée à autre chose.

Deux semaines plus tard, un autre graphique était pire : les coûts E/S d’Aurora grimpaient pendant les pics, même si la latence semblait correcte. Le coupable était le nouvel index combiné avec un pattern d’écriture « innocent » : la colonne indexée était mise à jour fréquemment. Chaque écriture mettait maintenant à jour l’index, augmentant l’amplification d’écriture et le volume de WAL.

Sous haute concurrence, l’amplification d’écriture plus la concurrence accrue a généré plus de travail en arrière-plan : plus de WAL, plus de pression sur la réplication, plus de churn de cache. Le système respectait les SLO la plupart du temps, mais il faisait plus de travail total. Le compteur l’a remarqué. Il remarque toujours.

Ils ont corrigé en revenant en arrière : l’endpoint n’avait pas besoin de mettre à jour cette colonne indexée à chaque requête. Ils ont déplacé la mise à jour dans un processus batch et ajouté une table séparée pour les attributs très volatils. Ils ont conservé l’index, mais changé le comportement d’écriture. Le coût a baissé sans perdre les gains de performance.

Morale : une « optimisation de performance » qui ignore les patterns d’écriture n’est qu’une optimisation de coût pour votre fournisseur cloud.

Mini-histoire 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise

Une entreprise liée aux paiements avait des exigences de fiabilité strictes et une culture étonnamment ordinaire autour de la planification de capacité. Ils utilisaient Aurora PostgreSQL, mais le traitaient comme un système de production, pas comme un tour de magie. Chaque trimestre, ils faisaient un exercice de pic : rejouer du trafic, mesurer le mix de requêtes et vérifier les tableaux de bord de coûts et de performance.

Lors d’un exercice, ils ont remarqué un pattern : après un basculement, les caches étaient froids et les E/S montaient pendant environ 15 minutes. Ce n’était pas catastrophique, mais coûteux et cela pouvait empirer en cas d’incident réel. Plutôt que d’accepter, ils ont construit une routine de réchauffement : un ensemble contrôlé de requêtes représentatives exécutées à faible rythme après un basculement pour amorcer les caches et stabiliser la performance.

Ils avaient aussi une règle stricte : toute action d’urgence de scaling nécessitait un ticket de scale-down avec une échéance. Si quelqu’un ajoutait un replica ou augmentait la taille d’une instance, il y avait un rappel automatique et une revue obligatoire. Pas d’exceptions, parce que « temporaire » est le mot le plus durable dans l’infrastructure.

Des mois plus tard, un vrai pic de trafic a frappé : une intégration partenaire est devenue virale du jour au lendemain. Le système a encaissé la charge. Les routines de réchauffement ont réduit le churn d’E/S au démarrage pendant un basculement mineur. La politique de scale-down a empêché l’apparition de réplicas inutiles. Le directeur financier n’a jamais eu à apprendre ce qu’est une « requête E/S », ce qui est la vraie définition du succès opérationnel.

Erreurs courantes : symptôme → cause racine → correctif

1) Symptôme : les coûts E/S d’Aurora augmentent pendant les pics, même si le CPU semble correct

Cause racine : Hits de cache manqués, scans séquentiels, déversements sur disque ou régressions de plan causent beaucoup de lectures physiques.

Correctif : Utilisez pg_stat_statements + EXPLAIN (ANALYZE, BUFFERS) pour trouver les requêtes lourdes en lecture ; ajoutez des index, réduisez les jeux de résultats et corrigez les déversements temporaires. Évitez de scaler le compute comme premier réflexe si vous êtes limité par les E/S.

2) Symptôme : le stockage croît rapidement et ne semble pas réduire ensuite

Cause racine : Bloat MVCC, rétention WAL (slots de réplication / réplicas en retard), transactions longues ou grosses migrations.

Correctif : Éliminez les transactions longues, tunez autovacuum, corrigez le lag de réplication et planifiez la remédiation du bloat. Attendez-vous à ce que la « réduction » nécessite un travail délibéré, pas de l’espoir.

3) Symptôme : vous avez ajouté des réplicas de lecture mais la latence du writer s’est aggravée

Cause racine : Le goulet était les écritures ou les verrous, pas les lectures. Les réplicas ajoutent de la complexité et parfois de la pression supplémentaire (tempêtes de connexions, erreurs de routage).

Correctif : Confirmez la séparation lecture/écriture et les principaux wait events avant d’ajouter des réplicas. Si les écritures sont chaudes, optimisez-les, réduisez la contention et batcher.

4) Symptôme : Aurora Serverless v2 monte agressivement pendant des petites rafales

Cause racine : Tempêtes de connexions, boucles de retry ou endpoints bavards créent des signaux de demande qui ressemblent à une vraie charge.

Correctif : Ajoutez du pooling, limitez le débit, implémentez des budgets de retry avec jitter et réduisez le nombre de requêtes par requête.

5) Symptôme : après un basculement, coûts et latence grimpent pendant 10–30 minutes

Cause racine : Caches froids et instabilité des plans après les changements de rôle ; aussi tempêtes de reconnexion côté app.

Correctif : Requêtes de réchauffement, étalez les reconnexions, appliquez du backoff et maintenez les index et statistiques chauds et sains.

6) Symptôme : l’usage temporaire explose pendant les pics

Cause racine : Opérations de tri/hash déversées à cause de limites mémoire + forte concurrence.

Correctif : Réduisez les jeux de résultats, ajoutez des index, réécrivez les requêtes et plafonnez la concurrence via un pooler. Ajustez work_mem prudemment en tenant compte des limites de connexions.

7) Symptôme : « Nous avons monté, mais ça n’a pas beaucoup aidé »

Cause racine : Vous avez monté la mauvaise dimension. Le problème est souvent les verrous, les E/S ou le bavardage réseau — pas le CPU.

Correctif : Utilisez l’analyse des waits, les métriques de lectures de buffers et le diagnostic de verrous. Montez seulement après avoir pu nommer le goulet en une phrase.

8) Symptôme : les factures restent élevées après la fin du pic

Cause racine : Réplicas persistants, classe d’instance non revertie, croissance du stockage ou hausse de baseline due à de nouvelles fonctionnalités.

Correctif : Imposer des politiques de scale-down, effectuer des revues hebdomadaires des différences de facture et mesurer le QPS/mix de requêtes baseline vs le mois précédent. Traitez les régressions de coût comme des régressions de performance.

Listes de contrôle / plan étape par étape

Plan étape par étape pour prévenir les surprises de coût liées aux pics

  1. Définissez le pic : QPS maximal, concurrence maximale et les endpoints qui comptent. Si vous ne pouvez pas le définir, vous ne pouvez pas le budgéter.
  2. Instrumentez les bonnes métriques : requêtes par temps total, lectures de buffers, octets temporaires, compte de connexions, waits de verrous, lag des réplicas, volume WAL.
  3. Fixez des garde-fous : max connections, taille du pool, statement_timeout, idle_in_transaction_session_timeout.
  4. Écrivez des budgets de retry : retries avec backoff exponentiel + jitter, et un plafond par requête pour que votre app ne transforme pas un timeout en dix.
  5. Pré-calculer où ça paye : mettez en cache et matérialisez les lectures coûteuses qui surviennent pendant les pics. Privilégiez un compute prévisible à des E/S imprévisibles.
  6. Utilisez les réplicas intentionnellement : seulement lorsque le scaling en lecture est le vrai goulet ; définissez une règle d’auto scale-down ou une tâche de nettoyage post-incident explicite.
  7. Tunez l’autovacuum pour les tables chaudes : basez-le sur le churn observé, pas sur les valeurs par défaut. Les valeurs par défaut sont conservatrices, pas généreuses.
  8. Planifiez les basculements : tempêtes de connexions et caches froids font partie de la vie ; testez les patrons de réchauffement et le backoff des reconnexions.
  9. Budgétez par dimension : heures de compute, heures de réplica, E/S, croissance du stockage et rétention des sauvegardes. Les pics touchent plusieurs dimensions.
  10. Faites un exercice de pic trimestriel : rejouez le trafic, confirmez la performance, confirmez les drivers de coût. Traitez la facture comme un signal de monitoring.

Checklist d’urgence (pendant un pic)

  • Confirmez si la charge est du trafic réel ou des retries (vérifiez le taux d’erreurs applicatives et les compteurs de retry).
  • Vérifiez les connexions et sessions actives ; activez le pooling ou réduisez les tailles de pool immédiatement si nécessaire.
  • Trouvez les requêtes en tête par temps total et cherchez des régressions de plan ; appliquez des index sûrs ou des corrections de requête.
  • Cherchez les accumulations de verrous ; stoppez les DDL bloquants ; tuez les pire coupables si c’est sûr.
  • Si vous ajoutez des réplicas ou augmentez la taille, créez immédiatement un ticket de scale-down avec une échéance.

Checklist post-incident (le lendemain, quand les émotions sont plus calmes)

  • Comparez la fenêtre du pic à la baseline : mix de requêtes, octets temporaires, hit du cache, volume WAL.
  • Identifiez la « première domino » (déploiement, migration, trafic partenaire, alignement de cron).
  • Écrivez un changement préventif qui réduit l’amplification (pooling, cache, réécriture de requête).
  • Audit de changements topologiques (réplicas, tailles d’instance) et réversion des capacités temporaires.
  • Examinez les moteurs de croissance du stockage (bloat, rétention WAL, transactions longues) et planifiez la remédiation.

FAQ

Est-ce qu’Aurora est toujours plus cher que PostgreSQL standard ?

Non. Aurora peut être moins cher quand vous valorisez la durabilité managée, le basculement rapide et des opérations prévisibles — surtout si vous auriez autrement surprovisionné EC2 et stockage pour la fiabilité. Il peut être plus cher lorsque votre workload est lourd en I/O ou inefficace, car la facturation basée sur la consommation met immédiatement en lumière le gaspillage.

Quelle est la cause la plus fréquente des pics de coût ?

L’amplification de charge : retries + tempêtes de connexions + requêtes inefficaces. Une petite augmentation de latence déclenche des retries, ce qui augmente la charge, ce qui augmente la latence. Le compteur tourne pendant que vous debuggez.

Dois-je utiliser Aurora Serverless v2 pour des charges en pics ?

Utilisez-le quand vos pics sont légitimes et que vous avez déjà contrôlé le comportement des connexions. Si vos pics sont principalement auto-infligés (timeouts, retries, requêtes bavardes), le serverless s’adaptera fidèlement au chaos et vous facturera en conséquence.

Les réplicas de lecture réduisent-ils les frais E/S d’Aurora ?

Parfois, mais pas automatiquement. Les réplicas peuvent répartir la charge de lecture, mais ils peuvent aussi générer plus de travail total si vous faites un mauvais routage, réchauffez les caches à répétition ou laissez des réplicas sous-utilisés. Mesurez les E/S de lecture et la distribution des requêtes avant et après.

Pourquoi le stockage n’a-t-il pas diminué après la suppression des données ?

Dans PostgreSQL, les deletes créent des tuples morts ; l’espace devient réutilisable en interne, pas forcément retourné rapidement au stockage sous-jacent sans opérations lourdes. Dans Aurora, le récit « stockage auto-dimensionné » ne signifie pas une réduction instantanée. Planifiez la récupération d’espace comme un projet, pas un souhait.

Quels sont les meilleurs garde-fous pour contrôler les pics ?

Pooling des connexions, max_connections raisonnable, timeouts de statement et budgets de retry. Ceux-ci réduisent la cascade classique où la base devient plus lente, les clients paniquent et le système se dévore lui-même.

Comment savoir si je suis limité par le CPU ou par les E/S ?

Regardez les plans de requêtes avec BUFFERS, les octets temporaires et l’iowait au niveau hôte (pour l’auto-géré). Les incidents liés au CPU montrent un CPU élevé avec relativement peu de lectures physiques ; les incidents I/O-bound montrent des lectures de buffer significatives et des déversements temporaires, souvent avec un CPU modéré.

Puis-je plafonner directement les coûts d’Aurora ?

Vous pouvez plafonner les comportements qui créent des coûts : limiter les réplicas, plafonner la capacité minimale/maximale serverless, limiter les connexions et les retries. La plateforme ne vous empêchera pas de consommer des E/S ; votre architecture et vos garde-fous doivent le faire.

Quel est le « premier correctif » le plus sûr quand un pic survient ?

Réduire l’amplification. Limitez ou sheddez le débit de l’endpoint le plus mauvais, activez le pooling et ralentissez les reconnexions/retries. Ensuite optimisez les requêtes. Le scaling vient après, une fois que vous savez quoi scaler.

Quand devrais-je choisir PostgreSQL pur plutôt qu’Aurora ?

Choisissez l’auto-gestion (ou un Postgres managé simple) quand vous voulez un « paiement pour la capacité » prévisible, que vous avez une maturité opérationnelle forte et que votre workload est suffisamment stable pour accepter la surprovision. Choisissez Aurora quand vous valorisez le basculement rapide et la durabilité du stockage managé et que vous êtes prêt à concevoir des contrôles de coût autour de la consommation.

Conclusion : prochaines étapes qui réduisent vraiment les surprises

Les pics révèlent la vérité. Votre facture de base de données n’est qu’une des façons dont elle la rapporte.

Si vous hésitez entre PostgreSQL et Aurora PostgreSQL, ne réduisez pas la question à « managé vs non-managé ». Réduisez-la à quel mode de défaillance vous voulez combattre en premier. Avec Postgres auto-géré, vous lutterez contre la saturation et la capacité. Avec Aurora, vous combattrez l’amplification et la mesure. Vous pouvez gagner l’un ou l’autre combat, mais il vous faut des réflexes différents.

Prochaines étapes concrètes :

  1. Instrumentez la réalité au niveau requête : activez pg_stat_statements et construisez un tableau de bord pour les requêtes par temps total, les octets temporaires et les lectures de buffers.
  2. Mettez en place le pooling et des budgets de retry : traitez les connexions et les retries comme des contrôles de coût et de fiabilité à la fois.
  3. Tunez l’autovacuum sérieusement : identifiez les tables à churn élevé et donnez à l’autovacuum les ressources et seuils pour suivre.
  4. Écrivez une politique de scaling : les réplicas et les changements de taille d’instance doivent avoir un plan de scale-down avec un propriétaire et une échéance.
  5. Faites un exercice de pic : simulez votre pire heure, puis vérifiez à la fois les graphiques de latence et les drivers de coût. Si vous n’en vérifiez qu’un, l’autre vous surprendra.

Faites cela, et votre prochain pic restera stressant — la production est toujours stressante — mais au moins il n’arrivera pas avec un deuxième incident livré en PDF sous forme de facture.

← Précédent
Proxmox pveperf affiche des absurdités : comment benchmarker correctement
Suivant →
Liens d’ancrage façon site de documentation : icônes au survol, offsets et titres cliquables

Laisser un commentaire