mdbkit 0.3.0__tar.gz → 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mdbkit-0.3.0/mdbkit.egg-info → mdbkit-0.4.0}/PKG-INFO +321 -195
- mdbkit-0.3.0/PKG-INFO → mdbkit-0.4.0/README.md +864 -762
- {mdbkit-0.3.0 → mdbkit-0.4.0}/mdbkit/__init__.py +1 -1
- {mdbkit-0.3.0/mdbkit → mdbkit-0.4.0}/mdbkit/analysis.py +13 -0
- {mdbkit-0.3.0 → mdbkit-0.4.0}/mdbkit/cli.py +97 -13
- mdbkit-0.4.0/mdbkit/compare.py +175 -0
- {mdbkit-0.3.0/mdbkit → mdbkit-0.4.0}/mdbkit/ftdc.py +22 -0
- {mdbkit-0.3.0/mdbkit → mdbkit-0.4.0}/mdbkit/parser.py +43 -0
- {mdbkit-0.3.0/mdbkit → mdbkit-0.4.0}/mdbkit/render.py +114 -0
- {mdbkit-0.3.0/mdbkit → mdbkit-0.4.0}/mdbkit/triage.py +42 -5
- mdbkit-0.3.0/README.md → mdbkit-0.4.0/mdbkit.egg-info/PKG-INFO +888 -738
- mdbkit-0.4.0/mdbkit.egg-info/SOURCES.txt +31 -0
- {mdbkit-0.3.0 → mdbkit-0.4.0}/pyproject.toml +1 -1
- {mdbkit-0.3.0/mdbkit → mdbkit-0.4.0}/tests/test_demo_lab.py +167 -0
- {mdbkit-0.3.0 → mdbkit-0.4.0}/tests/test_ftdc.py +54 -0
- mdbkit-0.3.0/mdbkit/analysis.py +0 -567
- mdbkit-0.3.0/mdbkit/build/lib/mdbkit/__init__.py +0 -3
- mdbkit-0.3.0/mdbkit/build/lib/mdbkit/advisor.py +0 -319
- mdbkit-0.3.0/mdbkit/build/lib/mdbkit/analysis.py +0 -567
- mdbkit-0.3.0/mdbkit/build/lib/mdbkit/cli.py +0 -655
- mdbkit-0.3.0/mdbkit/build/lib/mdbkit/ftdc.py +0 -654
- mdbkit-0.3.0/mdbkit/build/lib/mdbkit/parser.py +0 -156
- mdbkit-0.3.0/mdbkit/build/lib/mdbkit/render.py +0 -336
- mdbkit-0.3.0/mdbkit/build/lib/mdbkit/triage.py +0 -810
- mdbkit-0.3.0/mdbkit/demo.py +0 -401
- mdbkit-0.3.0/mdbkit/explain.py +0 -281
- mdbkit-0.3.0/mdbkit/filtering.py +0 -93
- mdbkit-0.3.0/mdbkit/ftdc.py +0 -654
- mdbkit-0.3.0/mdbkit/lab.py +0 -385
- mdbkit-0.3.0/mdbkit/mdbkit/__init__.py +0 -3
- mdbkit-0.3.0/mdbkit/mdbkit/advisor.py +0 -319
- mdbkit-0.3.0/mdbkit/mdbkit/cli.py +0 -655
- mdbkit-0.3.0/mdbkit/mdbkit/demo.py +0 -401
- mdbkit-0.3.0/mdbkit/mdbkit/explain.py +0 -281
- mdbkit-0.3.0/mdbkit/mdbkit/filtering.py +0 -93
- mdbkit-0.3.0/mdbkit/mdbkit/lab.py +0 -385
- mdbkit-0.3.0/mdbkit/mdbkit/rebuild.py +0 -185
- mdbkit-0.3.0/mdbkit/mdbkit/report.py +0 -187
- mdbkit-0.3.0/mdbkit/mdbkit/scripts.py +0 -69
- mdbkit-0.3.0/mdbkit/parser.py +0 -156
- mdbkit-0.3.0/mdbkit/rebuild.py +0 -185
- mdbkit-0.3.0/mdbkit/render.py +0 -336
- mdbkit-0.3.0/mdbkit/report.py +0 -187
- mdbkit-0.3.0/mdbkit/scripts.py +0 -69
- mdbkit-0.3.0/mdbkit/tests/test_ftdc.py +0 -320
- mdbkit-0.3.0/mdbkit/triage.py +0 -810
- mdbkit-0.3.0/mdbkit.egg-info/SOURCES.txt +0 -66
- mdbkit-0.3.0/tests/test_demo_lab.py +0 -315
- mdbkit-0.3.0/tests/test_explain.py +0 -119
- mdbkit-0.3.0/tests/test_mdbkit.py +0 -331
- mdbkit-0.3.0/tests/test_rebuild_report.py +0 -160
- mdbkit-0.3.0/tests/test_triage.py +0 -173
- {mdbkit-0.3.0 → mdbkit-0.4.0}/LICENSE +0 -0
- {mdbkit-0.3.0 → mdbkit-0.4.0}/mdbkit/advisor.py +0 -0
- {mdbkit-0.3.0/mdbkit/build/lib → mdbkit-0.4.0}/mdbkit/demo.py +0 -0
- {mdbkit-0.3.0/mdbkit/build/lib → mdbkit-0.4.0}/mdbkit/explain.py +0 -0
- {mdbkit-0.3.0/mdbkit/build/lib → mdbkit-0.4.0}/mdbkit/filtering.py +0 -0
- {mdbkit-0.3.0/mdbkit/build/lib → mdbkit-0.4.0}/mdbkit/lab.py +0 -0
- {mdbkit-0.3.0/mdbkit/build/lib → mdbkit-0.4.0}/mdbkit/rebuild.py +0 -0
- {mdbkit-0.3.0/mdbkit/build/lib → mdbkit-0.4.0}/mdbkit/report.py +0 -0
- {mdbkit-0.3.0/mdbkit/build/lib → mdbkit-0.4.0}/mdbkit/scripts.py +0 -0
- {mdbkit-0.3.0 → mdbkit-0.4.0}/mdbkit.egg-info/dependency_links.txt +0 -0
- {mdbkit-0.3.0 → mdbkit-0.4.0}/mdbkit.egg-info/entry_points.txt +0 -0
- {mdbkit-0.3.0 → mdbkit-0.4.0}/mdbkit.egg-info/requires.txt +0 -0
- {mdbkit-0.3.0 → mdbkit-0.4.0}/mdbkit.egg-info/top_level.txt +0 -0
- {mdbkit-0.3.0 → mdbkit-0.4.0}/setup.cfg +0 -0
- {mdbkit-0.3.0/mdbkit → mdbkit-0.4.0}/tests/test_explain.py +0 -0
- {mdbkit-0.3.0/mdbkit → mdbkit-0.4.0}/tests/test_mdbkit.py +0 -0
- {mdbkit-0.3.0/mdbkit → mdbkit-0.4.0}/tests/test_rebuild_report.py +0 -0
- {mdbkit-0.3.0/mdbkit → mdbkit-0.4.0}/tests/test_triage.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mdbkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Offline toolkit for MongoDB 4.4+ structured logs: log analysis, slow-query shapes, connection churn, and deterministic index advice. A spiritual successor to mtools' log tools.
|
|
5
5
|
Author: Saqib Ameen Subhan
|
|
6
6
|
License: MIT
|
|
@@ -24,275 +24,267 @@ Dynamic: license-file
|
|
|
24
24
|
|
|
25
25
|
# mdbkit
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
[](https://github.com/saqibameen86/mdbkit/actions)
|
|
28
|
+
[](https://pypi.org/project/mdbkit/)
|
|
29
|
+
[](https://pypi.org/project/mdbkit/)
|
|
30
|
+
[](LICENSE)
|
|
28
31
|
|
|
29
|
-
|
|
32
|
+
**An offline toolkit for MongoDB structured logs** — slow-query analysis,
|
|
33
|
+
deterministic index advice, incident triage, and diagnostic-data decoding.
|
|
34
|
+
For MongoDB 4.4 – 8.0, from the terminal, without connecting to anything.
|
|
30
35
|
|
|
31
|
-
|
|
36
|
+
A spiritual successor to mtools' log tools, which never learned to read the
|
|
37
|
+
JSON log format introduced in 4.4.
|
|
32
38
|
|
|
33
|
-
|
|
39
|
+
```
|
|
40
|
+
namespace op count cumMs docsEx scan plan shape
|
|
41
|
+
shop.events aggregate 29 3.2m 25,810,000 98889:1 COLLSCAN+SORT {tenantId:eq, ts:gte} sort:{ts:-1}
|
|
42
|
+
shop.orders find 48 1.4m 6,000,000 2976:1 COLLSCAN+SORT {status:eq, createdAt:gt}
|
|
43
|
+
shop.users find 31 3.7s 31 1:1 IXSCAN{email} {email:eq}
|
|
44
|
+
```
|
|
34
45
|
|
|
35
|
-
|
|
46
|
+
Two of those need an index. One is already fine. That distinction is the
|
|
47
|
+
whole point.
|
|
36
48
|
|
|
37
|
-
|
|
49
|
+
---
|
|
38
50
|
|
|
39
|
-
## Try it
|
|
51
|
+
## Try it right now — no MongoDB required
|
|
40
52
|
|
|
41
|
-
|
|
42
|
-
|
|
53
|
+
`mdbkit demo` writes a realistic log containing a real incident, so you can
|
|
54
|
+
evaluate the tool in about a minute without touching a cluster:
|
|
43
55
|
|
|
44
56
|
```bash
|
|
45
57
|
pip install mdbkit
|
|
46
|
-
mdbkit demo --with-extras -o demo.log
|
|
47
58
|
|
|
48
|
-
mdbkit
|
|
49
|
-
|
|
50
|
-
mdbkit
|
|
59
|
+
mdbkit demo --with-extras -o demo.log # a log + indexes.json, schema.json, explain.json
|
|
60
|
+
|
|
61
|
+
mdbkit loginfo demo.log # what is in this log?
|
|
62
|
+
mdbkit queries demo.log # which query shapes cost the most?
|
|
63
|
+
mdbkit triage demo.log --window 0 # what went wrong, and when?
|
|
64
|
+
mdbkit connections demo.log # who connected, and did anyone fail to?
|
|
51
65
|
mdbkit advise demo.log --indexes indexes.json --schema schema.json
|
|
66
|
+
mdbkit explain explain.json # read a saved explain plan
|
|
52
67
|
```
|
|
53
68
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
69
|
+
The generated log contains a connection storm from one client, a replica set
|
|
70
|
+
election, an index build, five failed logins from a service account, and an
|
|
71
|
+
aggregation burning 25 million document reads to return 261 documents — then
|
|
72
|
+
`advise` tells you which index fixes it.
|
|
73
|
+
|
|
74
|
+
Output is deterministic: the same `--seed` always produces the same log, so a
|
|
75
|
+
demo behaves identically every time. Scenarios are `incident`, `healthy` (the
|
|
76
|
+
control case — useful for seeing what "nothing wrong" looks like) and `mixed`.
|
|
77
|
+
|
|
78
|
+
Ready for a real server? Jump to [the workflows](#the-four-questions-it-answers).
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Is it safe to run on a production server?
|
|
83
|
+
|
|
84
|
+
This is the right question to ask of any tool someone hands you. The honest
|
|
85
|
+
answer, and how to check it yourself.
|
|
86
|
+
|
|
87
|
+
**What mdbkit never does:**
|
|
88
|
+
|
|
89
|
+
| | |
|
|
90
|
+
|---|---|
|
|
91
|
+
| Connect to your database | Analysis commands read **files**. There is no driver, no URI, no connection. |
|
|
92
|
+
| Send anything anywhere | There is no network code at all. No telemetry, no update check, no crash reporting. |
|
|
93
|
+
| Change anything | It is strictly read-only. Where an action would help, it **prints the command** for you to review and run. |
|
|
94
|
+
| Execute what it reads | Log lines and explain files are parsed as data with `json.loads`. Nothing is ever evaluated. |
|
|
95
|
+
| Pull in dependencies | Zero runtime dependencies. Nothing in the supply chain but the Python standard library. |
|
|
96
|
+
|
|
97
|
+
**Verify it yourself in 60 seconds** — this is a small, dependency-free
|
|
98
|
+
codebase specifically so that you can:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# 1. No network, no shell-outs, no eval anywhere in the analysis code
|
|
102
|
+
pip show -f mdbkit | head -3
|
|
103
|
+
grep -rn "socket\|urllib\|requests\|http\|eval(\|exec(" $(python -c "import mdbkit,os;print(os.path.dirname(mdbkit.__file__))")
|
|
104
|
+
|
|
105
|
+
# 2. Confirm it has no dependencies
|
|
106
|
+
pip show mdbkit | grep Requires
|
|
107
|
+
|
|
108
|
+
# 3. Watch it make no connections while it runs (Linux)
|
|
109
|
+
strace -f -e trace=network mdbkit queries mongod.log 2>&1 | grep -c socket
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The grep returns nothing for every analysis module. The only file that starts
|
|
113
|
+
a process is `lab.py`, which exists to create a *throwaway test cluster* and
|
|
114
|
+
is documented as an explicit exception below.
|
|
115
|
+
|
|
116
|
+
**What it does read:** the log file you point it at; optionally
|
|
117
|
+
`diagnostic.data` (metrics only, never documents); optionally `indexes.json`
|
|
118
|
+
and `schema.json` that **you** generate with scripts mdbkit prints for you to
|
|
119
|
+
inspect first. On the database host it also reads `/proc` and calls `statvfs`
|
|
120
|
+
for disk and memory figures — nothing that leaves the machine.
|
|
121
|
+
|
|
122
|
+
**What leaves your machine: nothing.** There is no server to send it to.
|
|
123
|
+
|
|
124
|
+
**Still cautious?** That is reasonable. Run `mdbkit demo` first and see what
|
|
125
|
+
the output looks like on synthetic data, or `mdbkit lab` to try it against a
|
|
126
|
+
disposable local cluster before you point it at anything real. Both exist for
|
|
127
|
+
exactly this reason.
|
|
128
|
+
|
|
129
|
+
Full detail: [SECURITY.md](SECURITY.md).
|
|
130
|
+
|
|
131
|
+
---
|
|
57
132
|
|
|
58
133
|
## Install
|
|
59
134
|
|
|
60
|
-
**
|
|
135
|
+
**Most systems:**
|
|
61
136
|
```bash
|
|
62
137
|
pip install mdbkit
|
|
63
138
|
```
|
|
64
139
|
|
|
65
140
|
**Ubuntu 20.04 / Debian / Amazon Linux 2 (Python 3.8 hosts):**
|
|
66
141
|
```bash
|
|
67
|
-
|
|
68
|
-
sudo apt install pipx # Ubuntu/Debian
|
|
69
|
-
# or: sudo dnf install pipx # RHEL/Rocky/Amazon Linux
|
|
70
|
-
|
|
71
|
-
# Step 2: install mdbkit
|
|
142
|
+
sudo apt install pipx # or: sudo dnf install pipx
|
|
72
143
|
pipx install mdbkit
|
|
73
|
-
|
|
74
|
-
# Step 3: if pipx is not on your PATH yet
|
|
75
144
|
pipx ensurepath && source ~/.bashrc
|
|
76
145
|
```
|
|
77
146
|
|
|
78
|
-
**
|
|
147
|
+
**Modern Ubuntu/Debian complaining about "externally-managed-environment":**
|
|
79
148
|
```bash
|
|
80
149
|
pip install mdbkit --break-system-packages
|
|
81
150
|
```
|
|
82
151
|
|
|
83
|
-
**Air-gapped database hosts
|
|
152
|
+
**Air-gapped database hosts:**
|
|
84
153
|
```bash
|
|
85
|
-
#
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
# Copy the ./wheels folder to the database host, then run
|
|
154
|
+
pip download mdbkit -d ./wheels # on a connected machine
|
|
155
|
+
# copy ./wheels across, then:
|
|
89
156
|
pip install --no-index --find-links ./wheels mdbkit
|
|
90
157
|
```
|
|
91
158
|
|
|
92
|
-
**Upgrading
|
|
159
|
+
**Upgrading:**
|
|
93
160
|
```bash
|
|
94
|
-
pip install --upgrade mdbkit
|
|
95
|
-
|
|
96
|
-
mdbkit --version # confirm
|
|
161
|
+
pip install --upgrade mdbkit # or: pipx upgrade mdbkit
|
|
162
|
+
mdbkit --version
|
|
97
163
|
```
|
|
98
|
-
mdbkit never updates itself and never checks for updates — it makes no network
|
|
99
|
-
calls at all. Upgrades are always explicit.
|
|
100
164
|
|
|
101
|
-
Requires Python 3.8+.
|
|
165
|
+
Requires Python 3.8+. mdbkit never updates itself and never checks for
|
|
166
|
+
updates — upgrades are always explicit.
|
|
102
167
|
|
|
103
|
-
> mdbkit is a Python package
|
|
104
|
-
> via `apt install`, `dnf install`, or `yum install` — use `pip` or `pipx`.
|
|
168
|
+
> mdbkit is a Python package on PyPI. It is **not** in `apt`/`dnf`/`yum`.
|
|
105
169
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
```bash
|
|
109
|
-
# Overall log summary: versions, restarts, connection counts, error/warning totals
|
|
110
|
-
mdbkit loginfo /var/log/mongodb/mongod.log
|
|
111
|
-
|
|
112
|
-
# Slow queries grouped by query shape (literals stripped), ranked by total time
|
|
113
|
-
mdbkit queries mongod.log
|
|
114
|
-
mdbkit queries mongod.log --sort scanRatio --limit 10 --json
|
|
115
|
-
|
|
116
|
-
# Columns: cumMs = time summed across ALL occurrences of that shape (not one
|
|
117
|
-
# query); docsEx = documents examined; scan = examined per doc returned;
|
|
118
|
-
# plan = the plan MongoDB chose (COLLSCAN / IXSCAN{fields} / +SORT)
|
|
170
|
+
---
|
|
119
171
|
|
|
120
|
-
|
|
121
|
-
mdbkit connections mongod.log
|
|
172
|
+
## The four questions it answers
|
|
122
173
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
mdbkit filter mongod.log --severity E --last 20 # 20 most recent errors
|
|
126
|
-
mdbkit filter mongod.log --slow 200 --limit 50 # first 50 matches only
|
|
174
|
+
Every command reads files or stdin and accepts several files or a glob, so
|
|
175
|
+
rotated logs work as one stream: `mdbkit queries "mongod.log*"`.
|
|
127
176
|
|
|
128
|
-
|
|
129
|
-
mdbkit filter mongod.log --from 2026-07-01T08:00:00+04:00 --to 2026-07-01T09:00:00+04:00
|
|
130
|
-
mdbkit filter mongod.log --from 2026-07-01T08:00:00Z # UTC
|
|
131
|
-
mdbkit filter mongod.log --from 2026-07-01T08:00:00 # log's own timezone
|
|
132
|
-
mdbkit filter mongod.log --from 2026-07-01 | mdbkit queries -
|
|
177
|
+
### 1. Why is my database slow?
|
|
133
178
|
|
|
134
|
-
|
|
135
|
-
mdbkit queries mongod.log
|
|
179
|
+
```bash
|
|
180
|
+
mdbkit queries mongod.log # shapes ranked by total time
|
|
181
|
+
mdbkit queries mongod.log --sort scanRatio # worst examined:returned first
|
|
182
|
+
mdbkit queries mongod.log --shape 1 # full detail on one shape
|
|
136
183
|
```
|
|
137
184
|
|
|
138
|
-
|
|
185
|
+
`cumMs` is time summed across **all** occurrences of a shape, not one query.
|
|
186
|
+
`scan` is documents examined per document returned — `1:1` is healthy,
|
|
187
|
+
`98889:1` is a missing index. `plan` shows what MongoDB actually chose.
|
|
139
188
|
|
|
140
|
-
|
|
141
|
-
`mongosh` scripts you run yourself, so you can inspect exactly what they do
|
|
142
|
-
before running them. The exports contain **field names and types only — no
|
|
143
|
-
document values.**
|
|
189
|
+
### 2. What index would fix it?
|
|
144
190
|
|
|
145
|
-
**Generate the export scripts:**
|
|
146
191
|
```bash
|
|
192
|
+
# Optional but much sharper: export what already exists.
|
|
147
193
|
mdbkit export-script indexes > export_indexes.js
|
|
148
194
|
mdbkit export-script schema > export_schema.js
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
**Run them against your database** (replace with your actual host, port, and credentials):
|
|
152
|
-
```bash
|
|
153
|
-
# Basic (local, no auth):
|
|
154
|
-
mongosh --quiet "mongodb://localhost/yourdb" export_indexes.js > indexes.json
|
|
155
|
-
mongosh --quiet "mongodb://localhost/yourdb" export_schema.js > schema.json
|
|
156
195
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
--host your_db_host \
|
|
160
|
-
--port 27017 \
|
|
161
|
-
--username your_username \
|
|
162
|
-
--password your_password \
|
|
196
|
+
mongosh --quiet --host your_db_host --port 27017 \
|
|
197
|
+
--username your_username --password your_password \
|
|
163
198
|
--authenticationDatabase admin \
|
|
164
|
-
--eval "$(cat export_indexes.js)" > indexes.json
|
|
199
|
+
--eval "$(cat export_indexes.js)" > indexes.json # repeat for schema
|
|
165
200
|
|
|
166
|
-
|
|
167
|
-
--host your_db_host \
|
|
168
|
-
--port 27017 \
|
|
169
|
-
--username your_username \
|
|
170
|
-
--password your_password \
|
|
171
|
-
--authenticationDatabase admin \
|
|
172
|
-
--eval "$(cat export_schema.js)" > schema.json
|
|
201
|
+
mdbkit advise mongod.log --indexes indexes.json --schema schema.json --ns shop.orders
|
|
173
202
|
```
|
|
174
203
|
|
|
175
|
-
|
|
176
|
-
|
|
204
|
+
Every recommendation states the evidence it reasoned from, a confidence
|
|
205
|
+
level, the caveats, and how to validate it. It says *candidate*, not
|
|
206
|
+
*command*, and it never tells you to drop an index.
|
|
177
207
|
|
|
178
|
-
###
|
|
208
|
+
### 3. What happened at 3am?
|
|
179
209
|
|
|
180
210
|
```bash
|
|
181
|
-
|
|
182
|
-
mdbkit
|
|
211
|
+
mdbkit triage /var/log/mongodb/mongod.log # last 60 minutes by default
|
|
212
|
+
mdbkit triage mongod.log --window 0 # the whole file
|
|
213
|
+
mdbkit triage mongod.log --report incident.html # something to attach to a ticket
|
|
214
|
+
```
|
|
183
215
|
|
|
184
|
-
|
|
185
|
-
|
|
216
|
+
Cluster health, elections, connection storms, hot collections, index builds,
|
|
217
|
+
error clusters, slow-query peaks — plus disk, memory, CPU and FTDC metrics
|
|
218
|
+
when run on the database host. Every finding ends with the next command to
|
|
219
|
+
run.
|
|
186
220
|
|
|
187
|
-
|
|
188
|
-
|
|
221
|
+
### 4. Did my change actually help?
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
mdbkit compare before.log --after after.log
|
|
189
225
|
```
|
|
190
226
|
|
|
191
|
-
Sample output:
|
|
192
227
|
```
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
validate : Re-run the query with .explain('executionStats') ...
|
|
228
|
+
slow-query time DOWN 32% (6.0m -> 4.1m across compared shapes)
|
|
229
|
+
shapes: 1 improved, 0 regressed, 0 new, 0 gone, 4 unchanged
|
|
230
|
+
|
|
231
|
+
IMPROVED
|
|
232
|
+
shop.orders {createdAt:gt, status:eq} sort:{createdAt:-1}
|
|
233
|
+
mean 1.7s -> 33ms (-98%) scan 2976:1 -> 1:1 [COLLSCAN -> index, in-memory sort gone]
|
|
200
234
|
```
|
|
201
235
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
auto-drops). With `--schema`, it warns about array (multikey) fields,
|
|
205
|
-
low-cardinality booleans, and field-name typos, and adjusts confidence
|
|
206
|
-
accordingly.
|
|
236
|
+
The natural follow-up to `advise`: you created the index, a day passed, and
|
|
237
|
+
this tells you whether it worked.
|
|
207
238
|
|
|
208
|
-
|
|
239
|
+
**Bonus — who connected?**
|
|
209
240
|
|
|
210
241
|
```bash
|
|
211
|
-
mdbkit
|
|
212
|
-
mdbkit triage mongod.log --window 30 # last 30 minutes
|
|
213
|
-
mdbkit triage mongod.log --window 0 # whole file
|
|
214
|
-
mdbkit triage mongod.log --dbpath /data/db # if auto-discovery misses it
|
|
215
|
-
mdbkit triage mongod.log --no-sysprobe # analyzing a log copied off-host
|
|
242
|
+
mdbkit connections mongod.log
|
|
216
243
|
```
|
|
217
244
|
|
|
218
|
-
|
|
219
|
-
|
|
245
|
+
Per-IP churn with first/last seen, plus an authenticated-users table showing
|
|
246
|
+
successful and failed logins per account and when each last authenticated —
|
|
247
|
+
the question that starts most access incidents.
|
|
220
248
|
|
|
221
|
-
|
|
222
|
-
state, heartbeat failures, and whether the node is still serving
|
|
223
|
-
- restarts, error clusters, election/stepdown events
|
|
224
|
-
- connection storms — with the peak minute and the top source IPs
|
|
225
|
-
- slow-query volume and the peak minute, so you know *when* it hurt
|
|
226
|
-
- COLLSCAN share of slow operations (the missing-index signal)
|
|
227
|
-
- the hot collection plus its top three query shapes inline
|
|
228
|
-
- index-build activity (a common cause of surprise load)
|
|
229
|
-
- slow checkpoints, cache eviction pressure, flow control
|
|
230
|
-
- disk / memory / CPU load, and the running mongod's RSS and uptime
|
|
231
|
-
|
|
232
|
-
When run on the database host, mdbkit finds `dbPath` automatically — from the
|
|
233
|
-
log's startup line, or the running `mongod` process, or `/etc/mongod.conf`,
|
|
234
|
-
or common defaults — so the disk check works even when the current log has no
|
|
235
|
-
startup event. **`diagnostic.data` lives inside the dbPath, so it is picked up
|
|
236
|
-
automatically too**: you only need `--ftdc` to point somewhere else, such as a
|
|
237
|
-
directory copied off another host. All probing is stdlib-only (`/proc`,
|
|
238
|
-
`statvfs`); no shell-outs, nothing leaves the machine.
|
|
239
|
-
|
|
240
|
-
Cluster health is derived entirely from the log — no connection to the
|
|
241
|
-
database. It reports what the node last said about itself and its peers, which
|
|
242
|
-
is the honest limit of an offline tool, and enough to answer "is this node
|
|
243
|
-
serving, and what does it think of the others?"
|
|
244
|
-
|
|
245
|
-
Read-only: it never connects to the database, and every finding ends with a
|
|
246
|
-
next step for a human to review. Detectors marked beta are pattern-matched and
|
|
247
|
-
clearly labeled while broader validation is pending.
|
|
249
|
+
---
|
|
248
250
|
|
|
249
|
-
|
|
251
|
+
## Trying it against a real cluster
|
|
250
252
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
+
`mdbkit lab` starts a **disposable local MongoDB** so you can test against a
|
|
254
|
+
real server without touching anything that matters.
|
|
253
255
|
|
|
254
256
|
```bash
|
|
255
|
-
#
|
|
256
|
-
mdbkit
|
|
257
|
+
mdbkit lab start # 3-node replica set on 127.0.0.1:28110-28112
|
|
258
|
+
mdbkit lab seed # 50k documents + a deliberately mixed workload
|
|
259
|
+
mdbkit queries $(mdbkit lab logs | head -1)
|
|
260
|
+
mdbkit lab destroy --yes # remove it entirely
|
|
257
261
|
```
|
|
258
262
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
aggregate explains, classic and SBE (6.0+) plans, and sharded winning plans.
|
|
264
|
-
|
|
265
|
-
### Design principles
|
|
263
|
+
It binds to localhost only, uses ports far from 27017 so it can never be
|
|
264
|
+
confused with a real deployment, and refuses to touch any directory it did
|
|
265
|
+
not create. It is the one command that starts external processes — see
|
|
266
|
+
[SECURITY.md](SECURITY.md).
|
|
266
267
|
|
|
267
|
-
|
|
268
|
-
recommendation shows its evidence and its rule of reasoning.
|
|
269
|
-
* **Candidates, not commands.** mdbkit never tells you to blindly run
|
|
270
|
-
`createIndex`, and never advises dropping an index.
|
|
271
|
-
* **Honest about uncertainty.** Shapes seen once are labeled low-confidence;
|
|
272
|
-
`$or`, `$regex`, `$in` and low-selectivity operators carry explicit caveats.
|
|
273
|
-
* **Offline, always.** No network code exists in this codebase.
|
|
268
|
+
Full options and more examples: [`mdbkit lab`](#mdbkit-lab) in the reference.
|
|
274
269
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
Any MongoDB 4.4+ structured log: `mongod.log`, `mongos` logs, rotated `.gz`
|
|
278
|
-
files, or stdin (`-`). Slow-query lines (`"msg":"Slow query"`) are logged by
|
|
279
|
-
default for operations over `slowms` (100 ms); lower `slowms` or enable
|
|
280
|
-
profiling level 1 to capture more:
|
|
281
|
-
|
|
282
|
-
```js
|
|
283
|
-
db.setProfilingLevel(1, { slowms: 50 })
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
Pre-4.4 plain-text logs are detected and politely refused — for those, the
|
|
287
|
-
original mtools still works.
|
|
270
|
+
---
|
|
288
271
|
|
|
289
272
|
## Command reference
|
|
290
273
|
|
|
291
274
|
Every command reads files or stdin and writes to stdout. `--help` works on any
|
|
292
275
|
command (`mdbkit queries --help`). Global: `mdbkit --version`.
|
|
293
276
|
|
|
294
|
-
All commands that read a log accept
|
|
295
|
-
file, or `-` for stdin.
|
|
277
|
+
All commands that read a log accept one or more paths, a shell glob, a
|
|
278
|
+
rotated `.gz` file, or `-` for stdin. Several files are read as a single
|
|
279
|
+
stream in filename order, which matches MongoDB's rotation naming:
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
mdbkit queries mongod.log # one file
|
|
283
|
+
mdbkit queries mongod.log.1 mongod.log # explicit list
|
|
284
|
+
mdbkit queries "mongod.log*" # glob (quote it)
|
|
285
|
+
mdbkit queries /var/log/mongodb/mongod.log.*.gz # compressed archives
|
|
286
|
+
cat mongod.log | mdbkit queries - # stdin
|
|
287
|
+
```
|
|
296
288
|
|
|
297
289
|
---
|
|
298
290
|
|
|
@@ -341,6 +333,33 @@ query with different parameters is counted once.
|
|
|
341
333
|
mdbkit queries mongod.log
|
|
342
334
|
mdbkit queries mongod.log --sort scanRatio --limit 10
|
|
343
335
|
mdbkit queries mongod.log --min-ms 500 --json
|
|
336
|
+
mdbkit queries "mongod.log*" # rotated logs as one stream
|
|
337
|
+
mdbkit queries mongod.log --shape 1 # drill into the worst offender
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
`--shape N` expands one row of the table:
|
|
341
|
+
|
|
342
|
+
```
|
|
343
|
+
namespace : shop.events
|
|
344
|
+
shape : {tenantId:eq, ts:gte} sort:{ts:-1}
|
|
345
|
+
|
|
346
|
+
occurrences : 29
|
|
347
|
+
total time : 3.2m
|
|
348
|
+
mean / max : 6.6s / 8.9s
|
|
349
|
+
docs examined : 25,810,000
|
|
350
|
+
docs returned : 261
|
|
351
|
+
scan ratio : 98889 examined per document returned
|
|
352
|
+
|
|
353
|
+
plans observed
|
|
354
|
+
COLLSCAN 29x
|
|
355
|
+
|
|
356
|
+
flags
|
|
357
|
+
COLLSCAN — no index used for at least one execution
|
|
358
|
+
in-memory SORT — results sorted after retrieval
|
|
359
|
+
|
|
360
|
+
client applications
|
|
361
|
+
ReportWorker 15x
|
|
362
|
+
OrderService 7x
|
|
344
363
|
```
|
|
345
364
|
|
|
346
365
|
---
|
|
@@ -693,6 +712,66 @@ mdbkit advise $(mdbkit lab logs | head -1)
|
|
|
693
712
|
mdbkit lab destroy --yes # remove everything
|
|
694
713
|
```
|
|
695
714
|
|
|
715
|
+
**`mdbkit lab logs`** prints the log file path of every node, one per line,
|
|
716
|
+
so it composes with the other commands instead of you hunting for paths:
|
|
717
|
+
|
|
718
|
+
```bash
|
|
719
|
+
mdbkit lab logs
|
|
720
|
+
# /home/you/.mdbkit-lab/node0/mongod.log
|
|
721
|
+
# /home/you/.mdbkit-lab/node1/mongod.log
|
|
722
|
+
# /home/you/.mdbkit-lab/node2/mongod.log
|
|
723
|
+
|
|
724
|
+
mdbkit queries $(mdbkit lab logs | head -1) # just the primary
|
|
725
|
+
mdbkit triage $(mdbkit lab logs) # all three as one stream
|
|
726
|
+
mdbkit loginfo $(mdbkit lab logs | sed -n 2p) # a specific secondary
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
**A single node**, when you do not need replication — faster to start and it
|
|
730
|
+
does not require `mongosh`:
|
|
731
|
+
|
|
732
|
+
```bash
|
|
733
|
+
mdbkit lab start --standalone
|
|
734
|
+
mdbkit lab seed --docs 5000
|
|
735
|
+
mdbkit queries $(mdbkit lab logs)
|
|
736
|
+
mdbkit lab destroy --yes
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
**Several labs side by side**, for example to compare two MongoDB versions or
|
|
740
|
+
keep one running while you break another:
|
|
741
|
+
|
|
742
|
+
```bash
|
|
743
|
+
mdbkit lab start --dir ~/lab-a --port 28110
|
|
744
|
+
mdbkit lab start --dir ~/lab-b --port 28210 --standalone
|
|
745
|
+
|
|
746
|
+
mdbkit lab status --dir ~/lab-a
|
|
747
|
+
mdbkit lab destroy --dir ~/lab-b --yes
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
**Pause without losing data** — `stop` leaves the data directory intact so
|
|
751
|
+
you can start again later; only `destroy` deletes anything:
|
|
752
|
+
|
|
753
|
+
```bash
|
|
754
|
+
mdbkit lab stop # nodes down, data kept
|
|
755
|
+
mdbkit lab start # back up with the same data
|
|
756
|
+
mdbkit lab status # ports, pids, running or not
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
**A complete before/after experiment**, which is what `lab` is really for:
|
|
760
|
+
|
|
761
|
+
```bash
|
|
762
|
+
mdbkit lab start && mdbkit lab seed
|
|
763
|
+
cp $(mdbkit lab logs | head -1) before.log
|
|
764
|
+
|
|
765
|
+
mongosh --port 28110 --eval \
|
|
766
|
+
'db.getSiblingDB("shop").orders.createIndex({status:1, createdAt:-1})'
|
|
767
|
+
|
|
768
|
+
mdbkit lab seed # run the workload again with the index
|
|
769
|
+
cp $(mdbkit lab logs | head -1) after.log
|
|
770
|
+
|
|
771
|
+
mdbkit compare before.log --after after.log
|
|
772
|
+
mdbkit lab destroy --yes
|
|
773
|
+
```
|
|
774
|
+
|
|
696
775
|
`seed` runs indexed point lookups alongside deliberately unindexed queries —
|
|
697
776
|
an equality-plus-range-plus-sort with no supporting index, an aggregation
|
|
698
777
|
that scans the collection, and updates whose predicate has no index — so the
|
|
@@ -704,6 +783,44 @@ It is a laptop and scratch-VM tool, not a deployment tool.
|
|
|
704
783
|
|
|
705
784
|
---
|
|
706
785
|
|
|
786
|
+
### `mdbkit compare BEFORE --after AFTER`
|
|
787
|
+
|
|
788
|
+
Diffs query shapes between two logs and reports what improved, what
|
|
789
|
+
regressed, and what is new. The natural follow-up to `advise`: you created an
|
|
790
|
+
index, a day passed, and this answers whether it worked.
|
|
791
|
+
|
|
792
|
+
| Option | Default | Description |
|
|
793
|
+
|---|---|---|
|
|
794
|
+
| `--after FILE...` | required | The log(s) from after the change |
|
|
795
|
+
| `--ns NAMESPACE` | all | Compare only one namespace |
|
|
796
|
+
| `--min-count N` | 3 | Ignore shapes seen fewer than N times, so noise in a quiet log does not read as a regression |
|
|
797
|
+
| `--min-ms N` | 0 | Ignore operations faster than this |
|
|
798
|
+
| `--limit N` | 15 | Shapes to print (`0` = all) |
|
|
799
|
+
| `--include-system` | off | Include internal `admin`/`config`/`local` namespaces |
|
|
800
|
+
| `--report FILE` | | Write a shareable `.md` or `.html` report |
|
|
801
|
+
| `--json` | | Machine-readable output |
|
|
802
|
+
|
|
803
|
+
```bash
|
|
804
|
+
mdbkit compare before.log --after after.log
|
|
805
|
+
mdbkit compare before.log --after after.log --ns shop.orders
|
|
806
|
+
mdbkit compare "old/mongod.log*" --after "new/mongod.log*" --report change.html
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
```
|
|
810
|
+
slow-query time DOWN 32% (6.0m -> 4.1m across compared shapes)
|
|
811
|
+
shapes: 1 improved, 0 regressed, 0 new, 0 gone, 4 unchanged
|
|
812
|
+
|
|
813
|
+
IMPROVED
|
|
814
|
+
shop.orders {createdAt:gt, status:eq} sort:{createdAt:-1}
|
|
815
|
+
mean 1.7s -> 33ms (-98%) scan 2976:1 -> 1:1 [COLLSCAN -> index, in-memory sort gone]
|
|
816
|
+
```
|
|
817
|
+
|
|
818
|
+
A shape counts as improved or regressed on a plan change (COLLSCAN becoming
|
|
819
|
+
an index scan, or the reverse), on an in-memory sort disappearing, or on mean
|
|
820
|
+
duration moving by more than 20%.
|
|
821
|
+
|
|
822
|
+
---
|
|
823
|
+
|
|
707
824
|
### `mdbkit export-script {schema|indexes}`
|
|
708
825
|
|
|
709
826
|
Prints a small `mongosh` script to stdout. **mdbkit never connects to your
|
|
@@ -723,17 +840,26 @@ mdbkit export-script schema > export_schema.js
|
|
|
723
840
|
Terminal output is and will remain first-class — this tool is built for the
|
|
724
841
|
Linux box the database actually runs on.
|
|
725
842
|
|
|
726
|
-
**Shipped in v0.
|
|
727
|
-
top of v0.2's FTDC decoding, incident triage,
|
|
728
|
-
shareable reports.
|
|
729
|
-
|
|
730
|
-
Next up:
|
|
731
|
-
|
|
732
|
-
*
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
843
|
+
**Shipped in v0.4:** `compare`, rotated-log globbing, per-shape drill-down —
|
|
844
|
+
on top of v0.3's `demo` and `lab`, and v0.2's FTDC decoding, incident triage,
|
|
845
|
+
query reconstruction and shareable reports.
|
|
846
|
+
|
|
847
|
+
Next up, roughly in order:
|
|
848
|
+
|
|
849
|
+
* **Sharded clusters.** `mongos` logs are a different shape, and the classic
|
|
850
|
+
sharded failure — a query with no shard key fanning out to every shard — is
|
|
851
|
+
visible in the log. Also chunk migrations, balancer windows and jumbo
|
|
852
|
+
chunks. Would come with `mdbkit lab --sharded` so it can be tested.
|
|
853
|
+
* **Startup configuration audit.** mongod logs warnings at startup about
|
|
854
|
+
transparent huge pages, readahead, ulimits, NUMA and filesystem choice.
|
|
855
|
+
These are classic production misconfigurations and they are already in
|
|
856
|
+
your log — nothing new needs collecting.
|
|
857
|
+
* **Redundant index detection** from `indexes.json` alone: an index on
|
|
858
|
+
`{a: 1}` is redundant when `{a: 1, b: 1}` exists. Purely offline, no
|
|
859
|
+
connection, no `$indexStats` needed.
|
|
860
|
+
* **Confirming the FTDC-based checkpoint, eviction and flow-control
|
|
861
|
+
detectors** against real `diagnostic.data` — see
|
|
862
|
+
`docs/TESTING-PLAYBOOK.md`. Real logs and metrics very welcome.
|
|
737
863
|
|
|
738
864
|
mdbkit is validated against real-world structured logs (tens of thousands of
|
|
739
865
|
lines) in addition to its synthetic test fixtures.
|