Add PgBouncer to dev and PgBouncer stats to admin Infra page
- Add pgbouncer service to docker-compose-dev.yml with dev config - Route DATABASE_URL through pgbouncer:6432 in dev (matches prod) - Add PgBouncer pools and stats tables to /admin/infra with color-coded avg query/xact times and client waiting indicators - php, messenger, cron now depend on pgbouncer instead of database directly Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -109,6 +109,100 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{# PgBouncer #}
|
||||
<h2 class="text-xl font-black uppercase tracking-tighter italic heading-page mb-4">PgBouncer</h2>
|
||||
<div class="flex flex-wrap gap-6 mb-8">
|
||||
{% if pgbouncer.connected %}
|
||||
{# Pools #}
|
||||
<div class="flex-1 min-w-[500px]">
|
||||
<div class="admin-card">
|
||||
<h2 class="text-sm font-black uppercase tracking-widest mb-4">Pools</h2>
|
||||
{% if pgbouncer.pools|length > 0 %}
|
||||
<div class="overflow-x-auto">
|
||||
<table class="admin-table w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-[10px] font-black uppercase tracking-widest text-gray-300">Database</th>
|
||||
<th class="text-[10px] font-black uppercase tracking-widest text-gray-300">User</th>
|
||||
<th class="text-[10px] font-black uppercase tracking-widest text-gray-300 text-center">Mode</th>
|
||||
<th class="text-[10px] font-black uppercase tracking-widest text-gray-300 text-center">Clients actifs</th>
|
||||
<th class="text-[10px] font-black uppercase tracking-widest text-gray-300 text-center">Clients en attente</th>
|
||||
<th class="text-[10px] font-black uppercase tracking-widest text-gray-300 text-center">Serveurs actifs</th>
|
||||
<th class="text-[10px] font-black uppercase tracking-widest text-gray-300 text-center">Serveurs idle</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for pool in pgbouncer.pools %}
|
||||
<tr>
|
||||
<td class="font-black text-sm">{{ pool.database }}</td>
|
||||
<td class="text-sm">{{ pool.user }}</td>
|
||||
<td class="text-center"><span class="admin-badge-green text-xs font-black uppercase">{{ pool.pool_mode }}</span></td>
|
||||
<td class="text-center font-black">{{ pool.cl_active }}</td>
|
||||
<td class="text-center font-black {% if pool.cl_waiting > 0 %}text-yellow-600{% endif %}">{{ pool.cl_waiting }}</td>
|
||||
<td class="text-center font-black">{{ pool.sv_active }}</td>
|
||||
<td class="text-center font-black text-gray-400">{{ pool.sv_idle }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-sm text-gray-400">Aucun pool actif.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Stats #}
|
||||
<div class="flex-1 min-w-[500px]">
|
||||
<div class="admin-card">
|
||||
<h2 class="text-sm font-black uppercase tracking-widest mb-4">Stats</h2>
|
||||
{% if pgbouncer.stats|length > 0 %}
|
||||
<div class="overflow-x-auto">
|
||||
<table class="admin-table w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-[10px] font-black uppercase tracking-widest text-gray-300">Database</th>
|
||||
<th class="text-[10px] font-black uppercase tracking-widest text-gray-300 text-right">Transactions</th>
|
||||
<th class="text-[10px] font-black uppercase tracking-widest text-gray-300 text-right">Requetes</th>
|
||||
<th class="text-[10px] font-black uppercase tracking-widest text-gray-300 text-right">Avg xact (us)</th>
|
||||
<th class="text-[10px] font-black uppercase tracking-widest text-gray-300 text-right">Avg query (us)</th>
|
||||
<th class="text-[10px] font-black uppercase tracking-widest text-gray-300 text-right">Recu</th>
|
||||
<th class="text-[10px] font-black uppercase tracking-widest text-gray-300 text-right">Envoye</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for stat in pgbouncer.stats %}
|
||||
<tr>
|
||||
<td class="font-black text-sm">{{ stat.database }}</td>
|
||||
<td class="text-right text-sm font-bold">{{ stat.total_xact_count|number_format(0, '.', ' ') }}</td>
|
||||
<td class="text-right text-sm font-bold">{{ stat.total_query_count|number_format(0, '.', ' ') }}</td>
|
||||
<td class="text-right text-sm {% if stat.avg_xact_time > 100000 %}text-red-600{% elseif stat.avg_xact_time > 10000 %}text-yellow-600{% else %}text-green-600{% endif %} font-bold">{{ stat.avg_xact_time|number_format(0, '.', ' ') }}</td>
|
||||
<td class="text-right text-sm {% if stat.avg_query_time > 50000 %}text-red-600{% elseif stat.avg_query_time > 5000 %}text-yellow-600{% else %}text-green-600{% endif %} font-bold">{{ stat.avg_query_time|number_format(0, '.', ' ') }}</td>
|
||||
<td class="text-right text-sm text-gray-500">{{ (stat.total_received / 1048576)|number_format(1) }} MB</td>
|
||||
<td class="text-right text-sm text-gray-500">{{ (stat.total_sent / 1048576)|number_format(1) }} MB</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-sm text-gray-400">Aucune stat disponible.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="flex-1 min-w-[400px]">
|
||||
<div class="admin-card">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 class="text-sm font-black uppercase tracking-widest">PgBouncer</h2>
|
||||
<span class="admin-badge-red text-xs font-black uppercase">Deconnecte</span>
|
||||
</div>
|
||||
<p class="text-sm text-red-500 font-bold">{{ pgbouncer.error }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% macro row(label, value) %}
|
||||
|
||||
Reference in New Issue
Block a user