-
Notifications
You must be signed in to change notification settings - Fork 9
docs(analytics): document transaction queue depth metrics #573
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
169c5d9
e99504f
8ddf9ff
14ebc9e
d8a0dc6
a90548d
91d9fec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -171,14 +171,71 @@ Harper automatically tracks the following metrics for all services. Applications | |||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| ### Resource Usage Metrics | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| | `metric` | Key attributes | Other | Unit | Description | | ||||||||||||||||||||||||||||||||
| | ------------------------- | ------------------------------------------------------------------------------------------------ | ------------------- | ------- | --------------------------------------------------------------------------------- | | ||||||||||||||||||||||||||||||||
| | `database-size` | `size`, `used`, `free`, `audit` | `database` | bytes | Database file size breakdown | | ||||||||||||||||||||||||||||||||
| | `main-thread-utilization` | `idle`, `active`, `taskQueueLatency`, `rss`, `heapTotal`, `heapUsed`, `external`, `arrayBuffers` | `time` | various | Main thread resource usage: idle/active time, queue latency, and memory breakdown | | ||||||||||||||||||||||||||||||||
| | `resource-usage` | (see below) | | various | Node.js process resource usage (see [resource-usage](#resource-usage-metric)) | | ||||||||||||||||||||||||||||||||
| | `storage-volume` | `available`, `free`, `size` | `database` | bytes | Storage volume size breakdown | | ||||||||||||||||||||||||||||||||
| | `table-size` | `size` | `database`, `table` | bytes | Table file size | | ||||||||||||||||||||||||||||||||
| | `utilization` | | | % | Percentage of time the worker thread was processing requests | | ||||||||||||||||||||||||||||||||
| | `metric` | Key attributes | Other | Unit | Description | | ||||||||||||||||||||||||||||||||
| | ------------------------------- | ------------------------------------------------------------------------------------------------ | ------------------- | ------- | --------------------------------------------------------------------------------------------------------------------- | | ||||||||||||||||||||||||||||||||
| | `database-size` | `size`, `used`, `free`, `audit` | `database` | bytes | Database file size breakdown | | ||||||||||||||||||||||||||||||||
| | `main-thread-utilization` | `idle`, `active`, `taskQueueLatency`, `rss`, `heapTotal`, `heapUsed`, `external`, `arrayBuffers` | `time` | various | Main thread resource usage: idle/active time, queue latency, and memory breakdown | | ||||||||||||||||||||||||||||||||
| | `read-transaction-queue-depth` | `depth`, `maxDepth` | | count | Open tracked read transactions (see [transaction queue depth](#transaction-queue-depth-metrics)) | | ||||||||||||||||||||||||||||||||
| | `resource-usage` | (see below) | | various | Node.js process resource usage (see [resource-usage](#resource-usage-metric)) | | ||||||||||||||||||||||||||||||||
| | `storage-volume` | `available`, `free`, `size` | `database` | bytes | Storage volume size breakdown | | ||||||||||||||||||||||||||||||||
| | `table-size` | `size` | `database`, `table` | bytes | Table file size | | ||||||||||||||||||||||||||||||||
| | `transaction-commit-time` | `mean`, `median`, `p90`, `p95`, `p99`, `p999` | | ms | Duration from write commit submission to settlement (see [transaction queue depth](#transaction-queue-depth-metrics)) | | ||||||||||||||||||||||||||||||||
| | `utilization` | | | % | Percentage of time the worker thread was processing requests | | ||||||||||||||||||||||||||||||||
| | `write-transaction-queue-depth` | `depth`, `maxDepth` | | count | In-flight write transaction commits (see [transaction queue depth](#transaction-queue-depth-metrics)) | | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| #### Transaction Queue Depth Metrics | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| <VersionBadge version="v5.2.0" /> | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| `write-transaction-queue-depth` and `read-transaction-queue-depth` expose how many transactions are | ||||||||||||||||||||||||||||||||
| in flight against the storage engine per worker thread — a concurrency and throughput signal, not a | ||||||||||||||||||||||||||||||||
| reliable predictor on their own of the `Outstanding write transactions have too long of queue, please | ||||||||||||||||||||||||||||||||
| try again later` (HTTP 503) rejection: `maxDepth` amplitude reflects concurrent commits, not whether | ||||||||||||||||||||||||||||||||
| any single one is approaching the | ||||||||||||||||||||||||||||||||
| [`storage.maxTransactionQueueTime`](../database/storage-tuning.md#storagemaxtransactionqueuetime) | ||||||||||||||||||||||||||||||||
| duration limit (default 45s) that actually trips the 503. | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| `transaction-commit-time` records each commit's submit-to-settle duration on that same clock, and a | ||||||||||||||||||||||||||||||||
| rising `p99`/`p999` (in the `hdb_analytics` aggregate table, where percentiles are computed — they | ||||||||||||||||||||||||||||||||
| aren't present on `hdb_raw_analytics`) is a leading indicator of _gradual_ slowdowns approaching that | ||||||||||||||||||||||||||||||||
| limit. It doesn't help with a single commit that hangs indefinitely, though: the metric only records | ||||||||||||||||||||||||||||||||
| once a commit settles, so a genuinely wedged commit contributes no sample at all, while | ||||||||||||||||||||||||||||||||
| `write-transaction-queue-depth`'s `depth` stays elevated on that thread for as long as the commit | ||||||||||||||||||||||||||||||||
| remains outstanding. Harper also logs once per stuck commit when the 503 check itself fires, which is | ||||||||||||||||||||||||||||||||
| the authoritative signal for that specific failure. | ||||||||||||||||||||||||||||||||
|
Comment on lines
+198
to
+205
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With #572 owning the metric, this paragraph should shrink to the part that is genuinely specific to queue depth: the contrast between a settled-only distribution and a gauge that stays elevated while a commit is wedged. That contrast is the most useful thing in this section and it is not in #572, so keep it — just drop the raw/aggregate percentile aside, which #572 covers more completely (and which correctly lists all nine percentiles). The log sentence stays; I verified it against the source.
Suggested change
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| | Field | Unit | Description | | ||||||||||||||||||||||||||||||||
| | ---------- | ----- | ------------------------------------------------------- | | ||||||||||||||||||||||||||||||||
| | `depth` | count | Instantaneous depth sampled at emit time | | ||||||||||||||||||||||||||||||||
| | `maxDepth` | count | High-water mark since this thread's last emitted sample | | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| - **`write-transaction-queue-depth`** counts write commits handed to the storage engine whose commit | ||||||||||||||||||||||||||||||||
| promises have not yet settled — how many commits this thread is juggling concurrently. This is | ||||||||||||||||||||||||||||||||
| in-flight, not durability: under `storage.writeAsync: true` a settled commit promise does not | ||||||||||||||||||||||||||||||||
| guarantee the write has been synced to disk. | ||||||||||||||||||||||||||||||||
| - **`read-transaction-queue-depth`** counts concurrently open tracked read transactions, including | ||||||||||||||||||||||||||||||||
| ones opened with snapshot disabled. A high count can mean either many short-lived reads or a few | ||||||||||||||||||||||||||||||||
| long-lived ones — the count alone can't distinguish them, so use it as a concurrency signal; a | ||||||||||||||||||||||||||||||||
| duration-based metric would be needed to identify a single transaction held open long enough to | ||||||||||||||||||||||||||||||||
| hold back compaction. | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| Both metrics are gauges tracked only on the RocksDB write/read path, sampled per worker thread. On an | ||||||||||||||||||||||||||||||||
| LMDB-backed database (`storage.engine: lmdb`), `depth` and `maxDepth` for both metrics always read `0` | ||||||||||||||||||||||||||||||||
| — indistinguishable from a healthy, empty queue — regardless of actual read/write load. All per-thread | ||||||||||||||||||||||||||||||||
|
Comment on lines
+222
to
+224
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "On an LMDB-backed database ... always read
Suggested change
|
||||||||||||||||||||||||||||||||
| analytics reporting, including these gauges, piggybacks on the thread having recorded some other | ||||||||||||||||||||||||||||||||
| analytics-eligible activity in the period — a thread with no recordable activity in a given second | ||||||||||||||||||||||||||||||||
| emits no row at all rather than an explicit `depth: 0`. Absence of a sample is not the same as a | ||||||||||||||||||||||||||||||||
| healthy reading, particularly for `read-transaction-queue-depth` on an otherwise-quiet thread holding a | ||||||||||||||||||||||||||||||||
| single long-lived read. | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| The raw per-thread entries in `hdb_raw_analytics` retain each thread's true instantaneous `depth` and | ||||||||||||||||||||||||||||||||
| per-period `maxDepth`; treat those as the reliable source for spike detection. The aggregate | ||||||||||||||||||||||||||||||||
| `hdb_analytics` table is not a sum of per-thread peaks — each thread's `maxDepth` is first averaged | ||||||||||||||||||||||||||||||||
| across its raw samples for the period, then those per-thread averages are summed — so a brief | ||||||||||||||||||||||||||||||||
| single-thread spike is diluted rather than preserved. Always alert on `hdb_raw_analytics.maxDepth` (or | ||||||||||||||||||||||||||||||||
| lower the sampling/aggregation period) rather than relying on the aggregate table to catch short | ||||||||||||||||||||||||||||||||
| spikes. Tune the concrete alert threshold against a baseline for your workload, since absolute depth | ||||||||||||||||||||||||||||||||
| scales with worker-thread count and per-transaction size. | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| #### `resource-usage` Metric | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
transaction-commit-timebelongs in #572's### Storage Metricssection, not in this table — it isn't a resource-usage gauge. It flows throughrecordAction()with nobyThreadflag, so it aggregates as a cross-thread distribution, unlike every other row here. Dropping the row also resolves the incomplete and self-contradictory attribute list.