Usage Ingestion
Usage ingestion collects query logs from databases, parses them, aggregates usage counts per table, and publishes the results to OpenMetadata. It answers questions like “which tables are most queried?” and “who is using this table?”. Unlike metadata ingestion (Source → Sink), usage follows a 5-stage pipeline: Source → Processor → Stage → BulkSink.Pipeline Overview
Data Model Flow
Each stage transforms the data through a chain of models:Stage 1: Query Log Collection
UsageSource (source/database/usage_source.py) extends QueryParserSource and fetches raw query logs. Two modes:
Database Query Mode
Each database connector provides SQL to fetch from system views:
Configuration controls:
queryLogDuration— how many days back to look (default: 1)resultLimit— max queries to fetch (default: 100,000)filterCondition— custom SQL WHERE clause
File Mode
SetqueryLogFilePath to a CSV file containing query logs. The file is parsed into TableQuery objects.
Stage 2: SQL Parsing
QueryParserProcessor (processor/query_parser.py) calls parse_sql_statement() for each query.
Parsing Pipeline
LineageParser (lineage/parser.py) is shared with lineage ingestion — usage ingestion uses the table extraction capabilities, while lineage ingestion additionally uses the source/target direction analysis.
What Gets Extracted
From a query like:- tables:
["orders", "customers"] - joins:
{"orders": [{"columnName": "customer_id", "joinedWith": {"table": "customers", "column": "id"}}]} - query_type:
"SELECT"
Stage 3: Aggregation
TableUsageStage (stage/table_usage.py) groups parsed queries and writes to temporary files.
Aggregation Logic
Staging Files
Two types of JSON Lines files are written to the staging directory:- Usage files:
{serviceName}_{date}— oneTableUsageCountper line - Cost files:
{serviceName}_{date}_query— oneQueryCostWrapperper line
Stage 4: Publishing
MetadataUsageBulkSink (bulksink/metadata_usage.py) reads the staged files and publishes to OpenMetadata.
For Each Table Usage Record
- Resolve table entity — look up table by FQN via ES search + API fallback
- Publish usage count —
POST /api/v1/usage/table/{id}withUsageRequest(date, count) - Publish joins — send
TableJoinswith column-level join information (powers “Frequently Joined With” in the UI) - Create Query entities — create/link
Queryentities to the table - Update life cycle — infer created/modified/accessed dates from query types:
SELECT→ accessedINSERT/UPDATE→ modifiedCREATE→ created
After All Records
- Compute percentiles —
POST /api/v1/usage/compute.percentile/{entityType}/{date}ranks tables by usage
Query Cost Records
For databases that report cost (Snowflake, BigQuery):- Creates
QueryCostRecordentities linked to queries by hash - Tracks: total cost, execution count, total duration
Life Cycle Integration
LifeCycleQueryMixin (source/database/life_cycle_query_mixin.py) infers table lifecycle from query patterns:
Configuration
Performance
- Query hash caching — 5,000-entry LRU cache for query lookups
- Parser timeout — 30 seconds max per query
- Memory limits — 100MB max per query parsing session
- Staged processing — files written incrementally, not held in memory
- Batch size control — configurable
resultLimitper database