mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-10-17 18:19:04 +02:00
75 lines
4.2 KiB
Markdown
75 lines
4.2 KiB
Markdown
# MI package guidance
|
|
|
|
`Session.Query`, `QueryUnmarshal` and `QueryFunc` receive results through
|
|
`MI_OperationCallbacks` ([`callbacks.go`](callbacks.go)). Do not move them back
|
|
to the synchronous `MI_Operation_GetInstance` loop: when its result hand-over
|
|
waits, e.g. for parallel queries or slow providers such as `MSFT_StoragePool`,
|
|
the WMI client leaks Event handles (`miutils!RtlInterlockedCompareWait`).
|
|
`Test_MI_ParallelQuery_HandleGrowth` guards this. `QueryInstances` and
|
|
`Operation.GetInstance` remain as the synchronous low-level API for tests.
|
|
|
|
Rules for the callback path:
|
|
|
|
- Create native callbacks once (`sync.OnceValue`) and identify the query by
|
|
the callback context. `windows.NewCallback` never frees a callback and a
|
|
process can create only about 2000.
|
|
- Keep the callback context and the `MI_OperationCallbacks` pinned until
|
|
`MI_Operation_Close` has returned.
|
|
- Never call `Cancel` or `Close` from a callback. The goroutine that started
|
|
the query cancels on request and closes after the final callback
|
|
(`moreResults == MI_FALSE`).
|
|
- Recover panics in callbacks; they must not unwind into MI.
|
|
- Never block a callback on a goroutine. `QueryFunc` copies each instance
|
|
(`MI_Instance_Clone`) in the callback and runs `fn` for the copies on the
|
|
calling goroutine. Handing the original over and waiting for `fn` costs two
|
|
thread wake-ups per instance, about 25% more process CPU than the copy
|
|
(`Benchmark_MI_ProcessCPU`).
|
|
- MI reports parameter errors from within `MI_Session_QueryInstances`; the
|
|
runtime runs that callback on the calling goroutine. Never run caller code
|
|
such as the `QueryFunc` handler inside a callback: a panic cannot reach the
|
|
caller there and `runtime.Goexit` is fatal.
|
|
- While waiting for callbacks, keep a timer pending. Otherwise the runtime can
|
|
declare a false deadlock (golang/go#55015, still open in Go 1.27);
|
|
`Test_MI_Query_DeadlockDetector` reproduces it. Do not use a goroutine that
|
|
sleeps forever.
|
|
- Guard shared callback state with a mutex: results of one operation may
|
|
arrive on different MI threads. `-race` cannot reveal a missing lock there,
|
|
because the runtime treats every callback as synchronized with every native
|
|
call.
|
|
|
|
Query timeouts and options:
|
|
|
|
- Every query runs with an MI timeout. MI treats a zero timeout as unset, so
|
|
a non-positive timeout selects `DefaultQueryTimeout` (the session default
|
|
options) and a positive one below 1ms is raised to 1ms.
|
|
- Operation options must outlive every operation that uses them. Options for
|
|
whole-second timeouts are cached per session and shared by concurrent
|
|
operations; `Session.Close` deletes them after `MI_Session_Close` has
|
|
closed every operation. Other timeouts, e.g. the remaining scrape budget,
|
|
get per-query options that are deleted after the operation is closed.
|
|
- `startQuery` holds the read lock of `Session.lifecycle` from the `closed`
|
|
check until `MI_Session_QueryInstances` has returned; `Close` holds the
|
|
write lock across `MI_Session_Close` and the option deletion. Start every
|
|
callback query through `runQuery`/`startQuery`, never by calling
|
|
`asyncQuery.start` with session options directly.
|
|
|
|
Cancellation: `QueryContext` and `QueryFuncContext` do not start a query
|
|
for a done context. When it is done during the query, `wait` fails the query
|
|
through `fail`/`cancelRequested`, so `Cancel` runs on the waiting goroutine,
|
|
and still waits for the final callback before `close` frees anything. The
|
|
handler is not called after the context is done; a copy it skips fails the
|
|
query with the context's cause, even after the final result, so partial
|
|
results never look complete. A context that is done after the final result
|
|
(`complete`, set under `mu`) does not fail a query that has nothing left to
|
|
hand over.
|
|
|
|
Tests that need a slow query must bound the work it causes. Cancelling an
|
|
operation may end it only on the client: providers such as CIMWin32
|
|
(`CIM_DataFile`) keep enumerating in WmiPrvSE. `CIM_DataFile` for a whole drive
|
|
kept the CI runner busy for minutes after the Go tests, which slowed every
|
|
later step and made the end-to-end `/metrics` request time out. Restrict such
|
|
queries to one directory.
|
|
|
|
Run `CGO_ENABLED=1 go test -race` (gcc from w64devkit) and a
|
|
`-gcflags=all=-d=checkptr` run for changes in this package.
|