ForgeOpsTracker.QueryPlans (forge_ops_tracker v0.13.0)

Copy Markdown View Source

The pure pieces of the opt-in EXPLAIN (see ForgeOpsTracker.QueryPlanner): which statements are safe to EXPLAIN, the rate limits, masking a plan, and building the POST /api/v1/query_plans body. Also the db.system name for an Ecto adapter, and the data a database span carries.

Summary

Types

Rate limit state: last EXPLAIN time per masked statement, and recent EXPLAIN times.

Functions

Whether key (a masked statement) may be EXPLAINed at now_ms (a monotonic millisecond time): each key at most once per 10 minutes, and at most 10 per rolling minute overall. Returns the decision and the updated state.

The lowercase db.system for an Ecto adapter module (Ecto.Adapters.Postgres is "postgresql", MyXQL is "mysql", SQLite3 is "sqlite", Tds is "mssql") or a name given as a string, or nil when unknown.

Whether a statement is safe to EXPLAIN: after comments and leading whitespace it starts with SELECT, has no second statement (no ; except one at the very end), no locking clause (FOR UPDATE, FOR SHARE, FOR NO KEY UPDATE, FOR KEY SHARE), and no INSERT, UPDATE, DELETE or MERGE anywhere (a data-modifying CTE).

The masked statement a database span carries as "db.statement": SqlStatement.mask/1, capped at exactly 4000 characters.

Every string in the plan masked with SqlStatement.mask/1 (a plan's "Filter" or "Index Cond" repeats the query's literals); numbers, keys and structure unchanged.

The largest serialized plan that's sent; a bigger one is dropped.

Empty rate limit state.

The POST /api/v1/query_plans body, or nil when the plan isn't usable (not the JSON array Postgres returns) or its serialized form is over 64 KB. plan is that JSON text or already decoded.

The db system of an Ecto repo, from its adapter; nil when it can't be told.

data plus "db.statement" (already masked) and "db.system", each only when known.

The SET LOCAL statement_timeout value the EXPLAIN runs under.

Types

limits()

@type limits() :: %{
  statements: %{required(String.t()) => integer()},
  recent: [integer()]
}

Rate limit state: last EXPLAIN time per masked statement, and recent EXPLAIN times.

Functions

allow(map, key, now_ms)

@spec allow(limits(), String.t(), integer()) :: {boolean(), limits()}

Whether key (a masked statement) may be EXPLAINed at now_ms (a monotonic millisecond time): each key at most once per 10 minutes, and at most 10 per rolling minute overall. Returns the decision and the updated state.

db_system(name)

@spec db_system(module() | String.t() | nil) :: String.t() | nil

The lowercase db.system for an Ecto adapter module (Ecto.Adapters.Postgres is "postgresql", MyXQL is "mysql", SQLite3 is "sqlite", Tds is "mssql") or a name given as a string, or nil when unknown.

explainable?(statement)

@spec explainable?(String.t() | nil) :: boolean()

Whether a statement is safe to EXPLAIN: after comments and leading whitespace it starts with SELECT, has no second statement (no ; except one at the very end), no locking clause (FOR UPDATE, FOR SHARE, FOR NO KEY UPDATE, FOR KEY SHARE), and no INSERT, UPDATE, DELETE or MERGE anywhere (a data-modifying CTE).

mask_for_span(statement)

@spec mask_for_span(String.t() | nil) :: String.t() | nil

The masked statement a database span carries as "db.statement": SqlStatement.mask/1, capped at exactly 4000 characters.

mask_plan(value)

@spec mask_plan(term()) :: term()

Every string in the plan masked with SqlStatement.mask/1 (a plan's "Filter" or "Index Cond" repeats the query's literals); numbers, keys and structure unchanged.

max_plan_bytes()

@spec max_plan_bytes() :: pos_integer()

The largest serialized plan that's sent; a bigger one is dropped.

new_limits()

@spec new_limits() :: limits()

Empty rate limit state.

payload(config, masked_statement, db_system, plan, duration_ms, captured_at)

@spec payload(
  ForgeOpsTracker.Configuration.t(),
  String.t(),
  String.t(),
  term(),
  number(),
  DateTime.t()
) ::
  map() | nil

The POST /api/v1/query_plans body, or nil when the plan isn't usable (not the JSON array Postgres returns) or its serialized form is over 64 KB. plan is that JSON text or already decoded.

repo_db_system(repo)

@spec repo_db_system(module() | nil) :: String.t() | nil

The db system of an Ecto repo, from its adapter; nil when it can't be told.

span_data(masked_statement, db_system, data \\ %{})

@spec span_data(String.t() | nil, String.t() | module() | nil, map()) :: map()

data plus "db.statement" (already masked) and "db.system", each only when known.

statement_timeout()

@spec statement_timeout() :: String.t()

The SET LOCAL statement_timeout value the EXPLAIN runs under.