Find who's overloading your infrastructure
Project description
wholoads
Find who's overloading your infrastructure. Fast.
One command to answer "who is killing my system right now?" — whether it's PostgreSQL, ClickHouse, or Kubernetes.
$ wholoads pg
╭─────────────────────────────────────────────────────────╮
│ PostgreSQL — db-prod-01 (192.168.1.10) │
│ Uptime: 47d 3h │ Connections: 142/200 │ DB size: 89GB │
╰─────────────────────────────────────────────────────────╯
🔴 TOP CPU CONSUMERS
┏━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━┓
┃ # ┃ Query (truncated) ┃ Calls ┃ Total ┃ % of all ┃
┡━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━┩
│ 1 │ SELECT * FROM orders WHERE status... │ 1.2M │ 4h 12m │ 34.7% │
│ 2 │ UPDATE inventory SET quantity = ... │ 890K │ 2h 05m │ 17.1% │
│ 3 │ SELECT u.*, p.* FROM users u JO... │ 456K │ 1h 33m │ 12.8% │
└────┴───────────────────────────────────────┴──────────┴─────────┴──────────┘
🟡 WORST CACHE HIT RATIO
orders_archive: 23.4% (shared_blks_read: 4.2M)
audit_log: 45.1% (shared_blks_read: 1.8M)
💡 RECOMMENDATIONS
1. Query #1: Seq Scan on `orders` (2.1M rows) → CREATE INDEX CONCURRENTLY ...
2. Table `orders_archive`: cache hit 23% → consider partitioning or archival
3. 142/200 connections used → review connection pooling (pgbouncer)
$ wholoads ch
$ wholoads k8s --namespace production
Installation
pip install wholoads
Quick Start
# Generate a config template
wholoads init
# Edit connection settings
vim ~/.config/wholoads/config.yaml
# Run analysis
wholoads pg
wholoads ch
wholoads k8s
Configuration
~/.config/wholoads/config.yaml:
# PostgreSQL targets
postgresql:
targets:
- name: db-prod-01
# Connection method: direct | ssh
method: direct
host: 192.168.1.10
port: 5432
user: monitoring
password_env: WHOLOADS_PG_PASSWORD # read from env var
dbname: myapp
- name: db-prod-02
method: ssh
ssh_host: db-prod-02.internal
ssh_user: admin
ssh_key: ~/.ssh/id_ed25519
# psql runs as postgres user on the remote host
pg_user: postgres
dbname: zabbix
# Analysis settings
settings:
top_n: 10 # how many top queries per category
min_calls: 100 # ignore queries with fewer calls
cache_hit_threshold: 95.0 # flag tables below this %
explain: true # auto-run EXPLAIN for top queries
explain_format: json # text | json
# ClickHouse targets
clickhouse:
targets:
- name: ch-analytics
method: direct
host: ch-cluster.internal
port: 8123 # HTTP interface
user: readonly
password_env: WHOLOADS_CH_PASSWORD
- name: ch-datalayer
method: ssh
ssh_host: ch-datalayer-01.internal
ssh_user: admin
# uses clickhouse-client on remote host
settings:
top_n: 10
min_query_duration_ms: 1000
include_system_queries: false
# Kubernetes targets
kubernetes:
targets:
- name: prod-cluster
method: kubeconfig
context: prod-context
# or explicit kubeconfig path:
# kubeconfig: ~/.kube/prod.yaml
- name: staging
method: kubeconfig
context: staging-context
settings:
namespaces: [] # empty = all namespaces
exclude_namespaces:
- kube-system
- monitoring
sort_by: cpu # cpu | memory | restarts
top_n: 20
# Output settings
output:
format: rich # rich | json | csv | markdown
color: true
truncate_query: 80 # max query display length
# Global SSH defaults
ssh:
timeout: 10
known_hosts: ~/.ssh/known_hosts
# can be overridden per target
Plugins
PostgreSQL (wholoads pg)
Answers:
- Who consumes CPU? — top queries by
total_exec_timefrompg_stat_statements - Who reads disk? — top queries by
shared_blks_read - Who misses cache? — worst
cache_hit_ratioper query and per table - Who returns too much? — top queries by
rows / calls - Who holds locks? — long-running transactions and lock waits
- Who eats connections? — connections by user/application/state
Optional deep-dive: runs EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) on top queries and parses the plan for Seq Scans, estimation mismatches, disk sorts.
ClickHouse (wholoads ch)
Answers:
- Who consumes CPU? — top queries from
system.query_logbyquery_duration_ms - Who reads data? — top by
read_bytes/read_rows - Who from? — breakdown by
user,initial_address,client_name - Who writes? — top inserters by
written_bytes - What merges? — active merges and mutations from
system.merges/system.mutations - What's growing? — tables by size growth rate
Kubernetes (wholoads k8s)
Answers:
- Who eats CPU? — pods sorted by CPU usage vs requests/limits
- Who eats memory? — pods sorted by memory usage vs requests/limits
- Who restarts? — pods with high restart count, with last termination reason
- Who's pending? — unschedulable pods with reasons
- Who's throttled? — pods hitting CPU throttling
- Who has no limits? — pods running without resource limits (risky)
Output Formats
wholoads pg # rich terminal output (default)
wholoads pg --format json # JSON for piping
wholoads pg --format csv # CSV for spreadsheets
wholoads pg --format markdown # Markdown for reports/tickets
wholoads pg --format json | jq '.top_cpu[0]' # composable
Multiple Targets
wholoads pg # uses first target in config
wholoads pg --target db-prod-02 # specific target
wholoads pg --all # all configured PG targets
Writing Custom Plugins
from wholoads.plugin import BasePlugin, Finding, Severity
class RedisPlugin(BasePlugin):
name = "redis"
description = "Find who's overloading Redis"
def collect(self, target) -> list[Finding]:
# Connect and gather data
info = self.execute("INFO ALL")
clients = self.execute("CLIENT LIST")
slowlog = self.execute("SLOWLOG GET 20")
findings = []
# Analyze and produce findings
findings.append(Finding(
severity=Severity.RED,
category="memory",
title="Big key detected",
detail="key 'sessions:cache' is 2.1GB",
recommendation="Consider splitting or TTL"
))
return findings
Drop your plugin in ~/.config/wholoads/plugins/ and it's auto-discovered.
Contributing
Contributions welcome! See CONTRIBUTING.md for guidelines.
Priority areas:
- New plugins (Redis, MySQL, Nginx, RabbitMQ, MongoDB)
- Output formatters
- Connection methods
- Tests and CI
Support the Project
If wholoads saves you time during incidents, consider supporting development:
You can also:
- ⭐ Star the repo — it helps visibility
- 🐛 Report bugs and request features
- 📝 Write a plugin for your favorite system
- 📣 Share with colleagues who debug infrastructure
License
MIT — use it however you want.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file wholoads-0.2.0.tar.gz.
File metadata
- Download URL: wholoads-0.2.0.tar.gz
- Upload date:
- Size: 18.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b843130a6cd92fe9e88272332f911c15928879591579dd82e9dfa4a01ab16000
|
|
| MD5 |
428807161673e00c4aa68f5e99922351
|
|
| BLAKE2b-256 |
0a457c819a9ace984f02fe236a70cbdfd454cc2231943d8002681732cd66639d
|
Provenance
The following attestation bundles were made for wholoads-0.2.0.tar.gz:
Publisher:
ci.yml on timur-ND/wholoads
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
wholoads-0.2.0.tar.gz -
Subject digest:
b843130a6cd92fe9e88272332f911c15928879591579dd82e9dfa4a01ab16000 - Sigstore transparency entry: 1239235211
- Sigstore integration time:
-
Permalink:
timur-ND/wholoads@d91dd169e79eb777729d7b968964dfe378b1017f -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/timur-ND
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@d91dd169e79eb777729d7b968964dfe378b1017f -
Trigger Event:
push
-
Statement type:
File details
Details for the file wholoads-0.2.0-py3-none-any.whl.
File metadata
- Download URL: wholoads-0.2.0-py3-none-any.whl
- Upload date:
- Size: 19.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c1b3214d0ae758f96dccdfaf151da6113cee3beddfd5e65d19134a7a1792450f
|
|
| MD5 |
0f33dd1c009872c2af2b26e6327454d4
|
|
| BLAKE2b-256 |
60062776e8ffd0e767291628cd4bb45907a15b72dce2c174e3eb147a557c123b
|
Provenance
The following attestation bundles were made for wholoads-0.2.0-py3-none-any.whl:
Publisher:
ci.yml on timur-ND/wholoads
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
wholoads-0.2.0-py3-none-any.whl -
Subject digest:
c1b3214d0ae758f96dccdfaf151da6113cee3beddfd5e65d19134a7a1792450f - Sigstore transparency entry: 1239235212
- Sigstore integration time:
-
Permalink:
timur-ND/wholoads@d91dd169e79eb777729d7b968964dfe378b1017f -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/timur-ND
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@d91dd169e79eb777729d7b968964dfe378b1017f -
Trigger Event:
push
-
Statement type: